[evolution] Bug #240317 - Allow searching in subscribe dialog



commit 0b743a787cf5cc69b2521d41e1c7f5ec8a798101
Author: Milan Crha <mcrha redhat com>
Date:   Thu May 6 17:19:07 2010 +0200

    Bug #240317 - Allow searching in subscribe dialog

 mail/em-subscribe-editor.c |  509 ++++++++++++++++++++++++++++++++++----------
 mail/mail-dialogs.ui       |   98 ++++++++--
 2 files changed, 482 insertions(+), 125 deletions(-)
---
diff --git a/mail/em-subscribe-editor.c b/mail/em-subscribe-editor.c
index e1e0e1d..ebec3e2 100644
--- a/mail/em-subscribe-editor.c
+++ b/mail/em-subscribe-editor.c
@@ -35,6 +35,7 @@
 #include "e-util/e-account-utils.h"
 #include "e-util/e-util-private.h"
 
+#include "em-folder-utils.h"
 #include "em-subscribe-editor.h"
 
 #include "mail-config.h"
@@ -43,6 +44,15 @@
 
 #define d(x)
 
+enum {
+	COL_SUBSCRIBED = 0, /* G_TYPE_BOOLEAN */
+	COL_NAME,           /* G_TYPE_STRING  */
+	COL_INFO_NODE,      /* G_TYPE_POINTER */
+	COL_CAN_SELECT,     /* G_TYPE_BOOLEAN */
+	COL_ICON_NAME,      /* G_TYPE_STRING  */
+	N_COLUMNS
+};
+
 typedef struct _EMSubscribeEditor EMSubscribeEditor;
 struct _EMSubscribeEditor {
 	GQueue stores;
@@ -50,6 +60,9 @@ struct _EMSubscribeEditor {
 	gint busy;
 	guint busy_id;
 
+	gboolean is_filtering; /* whether filtering is active */
+	guint refilter_id;     /* source ID of a refilter action, after change in the filter edit */
+
 	struct _EMSubscribe *current; /* the current one, if any */
 
 	GtkDialog *dialog;
@@ -57,6 +70,10 @@ struct _EMSubscribeEditor {
 	GtkWidget *combobox;
 	GtkWidget *none_selected; /* 'please select a xxx' message */
 	GtkWidget *progress;
+	GtkWidget *filter_entry; /* when not empty, then it's filtering */
+	GtkWidget *expand_button;
+	GtkWidget *collapse_button;
+	GtkWidget *refresh_button;
 };
 
 typedef struct _EMSubscribe EMSubscribe;
@@ -75,13 +92,16 @@ struct _EMSubscribe {
 
 	GtkWidget *widget;	/* widget to show for this store */
 	GtkTreeView *tree;	/* tree, if we have it */
+	GtkTreeModel *tree_store; /* a tree store, used when not filtering */
+	GtkTreeModel *list_store; /* list store, used when filtering */
+	GSList *all_selectable; /* list of selectable info's, stored in the tree_store, in reverse order */
+
+	GSList *tree_expanded_paths; /* list of expanded paths in the tree model */
 
 	/* list of all returns from get_folder_info, accessed by other structures */
 	GSList *info_list;
 
-	/* pending LISTs, EMSubscribeNode's */
 	gint pending_id;
-	GQueue pending;
 
 	/* queue of pending UN/SUBSCRIBEs, EMsg's */
 	gint subscribe_id;
@@ -108,6 +128,80 @@ static void sub_editor_busy(EMSubscribeEditor *se, gint dir);
 static gint sub_queue_fill_level(EMSubscribe *sub, EMSubscribeNode *node);
 static void sub_selection_changed(GtkTreeSelection *selection, EMSubscribe *sub);
 
+static gboolean
+test_contains (const gchar *where, const gchar *what)
+{
+	gunichar c;
+	const gchar *at = what;
+
+	if (!what || !where)
+		return TRUE;
+
+	while (c = g_utf8_get_char_validated (where, -1), c != 0 && c != (gunichar) -1 && c != (gunichar) -2) {
+		if (g_utf8_get_char (at) == g_unichar_tolower (c)) {
+			at = g_utf8_next_char (at);
+			if (!at || !*at)
+				return TRUE;
+		} else {
+			at = what;
+		}
+		where = g_utf8_next_char (where);
+	}
+
+	return FALSE;
+}
+
+static void
+update_filtering_column (EMSubscribeEditor *se, struct _EMSubscribe *sub)
+{
+	gchar *text;
+	GtkTreeIter iter;
+	GtkTreeModel *list_store;
+	GSList *l;
+
+	g_return_if_fail (se != NULL);
+	g_return_if_fail (sub != NULL);
+	g_return_if_fail (g_utf8_validate (gtk_entry_get_text (GTK_ENTRY (se->filter_entry)), -1, NULL));
+
+	if (!sub->tree)
+		return;
+
+	if (gtk_tree_view_get_model (sub->tree) == sub->list_store)
+		gtk_tree_view_set_model (sub->tree, NULL);
+
+	text = g_utf8_strdown (gtk_entry_get_text (GTK_ENTRY (se->filter_entry)), -1);
+	list_store = sub->list_store;
+
+	gtk_list_store_clear (GTK_LIST_STORE (list_store));
+	for (l = sub->all_selectable; l; l = l->next) {
+		EMSubscribeNode *node = l->data;
+		gboolean bl;
+
+		if (!node || !node->path || !node->info)
+			continue;
+
+		bl = (!text || !*text || (node && node->info && node->info->full_name && test_contains (node->info->full_name, text)));
+		if (!bl)
+			continue;
+
+		gtk_list_store_prepend ((GtkListStore *)list_store, &iter);
+		gtk_list_store_set (GTK_LIST_STORE (list_store), &iter,
+			COL_SUBSCRIBED, (node->info->flags & CAMEL_FOLDER_SUBSCRIBED) != 0,
+			COL_NAME, node->info->full_name,
+			COL_INFO_NODE, node,
+			COL_CAN_SELECT, TRUE,
+			COL_ICON_NAME, em_folder_utils_get_icon_name (node->info->flags),
+			-1);
+	}
+
+	g_free (text);
+
+	if (!gtk_tree_view_get_model (sub->tree)) {
+		gtk_tree_view_set_model (sub->tree, sub->list_store);
+		gtk_tree_view_set_search_column (sub->tree, COL_NAME);
+	}
+}
+
 static void
 sub_node_free(EMSubscribeNode *node)
 {
@@ -133,8 +227,15 @@ sub_unref(EMSubscribe *sub)
 		d(printf("subscribe object finalised\n"));
 		/* we dont have to delete the "subscribe" task list, as it must be empty,
 		   otherwise we wouldn't be unreffed (intentional circular reference) */
+		if (sub->tree_store)
+			g_object_unref (sub->tree_store);
+		if (sub->list_store)
+			g_object_unref (sub->list_store);
 		if (sub->folders)
 			g_hash_table_destroy(sub->folders);
+		g_slist_free (sub->all_selectable);
+		g_slist_foreach (sub->tree_expanded_paths, (GFunc) gtk_tree_path_free, NULL);
+		g_slist_free (sub->tree_expanded_paths);
 		l = sub->info_list;
 		while (l) {
 			GSList *n = l->next;
@@ -192,13 +293,14 @@ sub_folder_done (struct _zsubscribe_msg *m)
 	}
 
 	/* make sure the tree view matches the correct state */
-	model = gtk_tree_view_get_model(m->sub->tree);
+	/* all actions are done on tree store, synced to list store */
+	model = m->sub->tree_store;
 	if (gtk_tree_model_get_iter_from_string(model, &iter, m->path)) {
 		issub = (m->node->info->flags & CAMEL_FOLDER_SUBSCRIBED) != 0;
-		gtk_tree_model_get(model, &iter, 0, &subscribed, 2, &node, -1);
-		if (node == m->node)
-			gtk_tree_store_set((GtkTreeStore *)model, &iter, 0, issub, -1);
-		else {
+		gtk_tree_model_get(model, &iter, COL_SUBSCRIBED, &subscribed, COL_INFO_NODE, &node, -1);
+		if (node == m->node) {
+			gtk_tree_store_set ((GtkTreeStore *)model, &iter, COL_SUBSCRIBED, issub, -1);
+		} else {
 			d(printf("node mismatch, or subscribe state changed failed\n"));
 		}
 	}
@@ -264,14 +366,15 @@ sub_subscribe_folder (EMSubscribe *sub, EMSubscribeNode *node, gint state, const
 
 /* ********************************************************************** */
 static void
-sub_fill_level(EMSubscribe *sub, CamelFolderInfo *info,  GtkTreeIter *parent, gint pending)
+sub_fill_levels (EMSubscribe *sub, CamelFolderInfo *info, GtkTreeIter *parent)
 {
 	CamelFolderInfo *fi;
 	GtkTreeStore *treestore;
 	GtkTreeIter iter;
 	EMSubscribeNode *node;
 
-	treestore = (GtkTreeStore *)gtk_tree_view_get_model(sub->tree);
+	treestore = (GtkTreeStore *) sub->tree_store;
+	g_return_if_fail (treestore != NULL);
 
 	/* first, fill a level up */
 	fi = info;
@@ -285,12 +388,25 @@ sub_fill_level(EMSubscribe *sub, CamelFolderInfo *info,  GtkTreeIter *parent, gi
 			node = g_malloc0(sizeof(*node));
 			node->info = fi;
 			state = (fi->flags & CAMEL_FOLDER_SUBSCRIBED) != 0;
-			gtk_tree_store_set(treestore, &iter, 0, state, 1, fi->name, 2, node, -1);
+			gtk_tree_store_set (treestore, &iter,
+				COL_SUBSCRIBED, state,
+				COL_NAME, fi->name,
+				COL_INFO_NODE, node,
+				COL_CAN_SELECT, (fi->flags & CAMEL_FOLDER_NOSELECT) == 0,
+				COL_ICON_NAME, em_folder_utils_get_icon_name (fi->flags),
+				-1);
+			if ((fi->flags & CAMEL_FOLDER_NOSELECT) == 0)
+				sub->all_selectable = g_slist_prepend (sub->all_selectable, node);
+			if (state) {
+				GtkTreePath *path = gtk_tree_model_get_path ((GtkTreeModel *)treestore, &iter);
+				gtk_tree_view_expand_to_path (sub->tree, path);
+				gtk_tree_path_free (path);
+			}
 			if ((fi->flags & CAMEL_FOLDER_NOINFERIORS) == 0)
 				node->path = gtk_tree_model_get_path((GtkTreeModel *)treestore, &iter);
 			g_hash_table_insert(sub->folders, fi->full_name, node);
 		} else if (node->path) {
-			gtk_tree_model_get_iter(gtk_tree_view_get_model(sub->tree), &iter, node->path);
+			gtk_tree_model_get_iter (GTK_TREE_MODEL (treestore), &iter, node->path);
 			known = TRUE;
 		}
 
@@ -303,18 +419,9 @@ sub_fill_level(EMSubscribe *sub, CamelFolderInfo *info,  GtkTreeIter *parent, gi
 			/* save time, if we have any children alread, dont re-scan */
 			if (fi->child) {
 				d(printf("scanning child '%s'\n", fi->child->full_name));
-				sub_fill_level(sub, fi->child, &iter, FALSE);
+				sub_fill_levels (sub, fi->child, &iter);
 			} else if (!(fi->flags & CAMEL_FOLDER_NOCHILDREN)) {
-				GtkTreeIter new_iter;
 				d(printf("flags: CAMEL_FOLDER_NOCHILDREN is not set '%s', known:%d\n", fi->full_name, known?1:0));
-				if (!known) {
-					gtk_tree_store_append(treestore, &new_iter, &iter);
-					gtk_tree_store_set(treestore, &new_iter, 0, 0, 1, "Loading...", 2, NULL, -1);
-				}
-			}
-			else {
-				if (pending)
-					g_queue_push_tail (&sub->pending, node);
 			}
 		} else {
 			d(printf("%s:%s: fi->flags & CAMEL_FOLDER_NOINFERIORS=%d\t node->path=[%p]\n",
@@ -341,21 +448,17 @@ struct _emse_folderinfo_msg {
 static void
 sub_folderinfo_exec (struct _emse_folderinfo_msg *m)
 {
-	gchar *pub_full_name=NULL;
-
 	if (m->seq == m->sub->seq) {
-		camel_operation_register(m->base.cancel);
-		m->info = camel_store_get_folder_info(m->sub->store, m->node?m->node->info->full_name:pub_full_name,
-						      CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL | CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST, &m->base.ex);
-		camel_operation_unregister(m->base.cancel);
+		camel_operation_register (m->base.cancel);
+		/* get the full folder tree for search ability */
+		m->info = camel_store_get_folder_info (m->sub->store, NULL, CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL | CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST | CAMEL_STORE_FOLDER_INFO_RECURSIVE, &m->base.ex);
+		camel_operation_unregister (m->base.cancel);
 	}
 }
 
 static void
 sub_folderinfo_done (struct _emse_folderinfo_msg *m)
 {
-	EMSubscribeNode *node;
-
 	m->sub->pending_id = -1;
 	if (m->sub->cancel || m->seq != m->sub->seq)
 		return;
@@ -369,17 +472,15 @@ sub_folderinfo_done (struct _emse_folderinfo_msg *m)
 		if (m->node) {
 			GtkTreeIter iter;
 
-			gtk_tree_model_get_iter(gtk_tree_view_get_model(m->sub->tree), &iter, m->node->path);
-			sub_fill_level(m->sub, m->info, &iter, FALSE);
+			gtk_tree_model_get_iter (m->sub->tree_store, &iter, m->node->path);
+			sub_fill_levels (m->sub, m->info, &iter);
 		} else {
-			sub_fill_level(m->sub, m->info, NULL, TRUE);
+			sub_fill_levels (m->sub, m->info, NULL);
 		}
-	}
 
-	/* check for more to do */
-	node = g_queue_pop_head (&m->sub->pending);
-	if (node)
-		sub_queue_fill_level(m->sub, node);
+		if (m->sub->editor->is_filtering)
+			update_filtering_column (m->sub->editor, m->sub);
+	}
 }
 
 static void
@@ -433,6 +534,21 @@ sub_queue_fill_level(EMSubscribe *sub, EMSubscribeNode *node)
 	return id;
 }
 
+static void
+update_buttons_sesitivity (EMSubscribeEditor *se)
+{
+	gboolean is_tree_model;
+
+	if (!se)
+		return;
+
+	is_tree_model = se->current && se->current->tree && !se->is_filtering;
+
+	gtk_widget_set_sensitive (se->expand_button, is_tree_model);
+	gtk_widget_set_sensitive (se->collapse_button, is_tree_model);
+	gtk_widget_set_sensitive (se->refresh_button, se->current && se->current->tree);
+}
+
 /* ********************************************************************** */
 
 /* (un) subscribes the current selection */
@@ -448,11 +564,28 @@ sub_subscribe_toggled(GtkCellRendererToggle *render, const gchar *spath, EMSubsc
 	d(printf("subscribe toggled?\n"));
 
 	if (gtk_tree_model_get_iter_from_string(model, &iter, spath)) {
-		gtk_tree_model_get(model, &iter, 0, &subscribed, 2, &node, -1);
+		gchar *free_path;
+
+		gtk_tree_model_get(model, &iter, COL_SUBSCRIBED, &subscribed, COL_INFO_NODE, &node, -1);
+		g_return_if_fail (node != NULL);
 		subscribed = !subscribed;
 		d(printf("new state is %s\n", subscribed?"subscribed":"not subscribed"));
-		gtk_tree_store_set((GtkTreeStore *)model, &iter, 0, subscribed, -1);
+		if (GTK_IS_TREE_STORE (model)) {
+			gtk_tree_store_set ((GtkTreeStore *)model, &iter, COL_SUBSCRIBED, subscribed, -1);
+		} else {
+			/* it's a list store, convert spath to tree path and update tree store value */
+			gtk_list_store_set (GTK_LIST_STORE (model), &iter, COL_SUBSCRIBED, subscribed, -1);
+			if (gtk_tree_model_get_iter (sub->tree_store, &iter, node->path)) {
+				gtk_tree_store_set ((GtkTreeStore *)sub->tree_store, &iter, COL_SUBSCRIBED, subscribed, -1);
+			}
+			free_path = gtk_tree_path_to_string (node->path);
+			if (subscribed)
+				sub->tree_expanded_paths = g_slist_prepend (sub->tree_expanded_paths, gtk_tree_path_copy (node->path));
+			spath = free_path;
+		}
+
 		sub_subscribe_folder(sub, node, subscribed, spath);
+		g_free (free_path);
 	}
 }
 
@@ -462,7 +595,7 @@ static void sub_do_changed(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *
 	EMSubscribeNode *node;
 	gboolean subscribed;
 
-	gtk_tree_model_get(model, iter, 0, &subscribed, 2, &node, -1);
+	gtk_tree_model_get(model, iter, COL_SUBSCRIBED, &subscribed, COL_INFO_NODE, &node, -1);
 
 	if (subscribed)
 		sub->selected_subscribed_count++;
@@ -488,52 +621,6 @@ static void sub_row_activated(GtkTreeView *tree, GtkTreePath *path, GtkTreeViewC
 }
 
 static void
-sub_row_expanded(GtkTreeView *tree, GtkTreeIter *iter, GtkTreePath *path, EMSubscribe *sub)
-{
-	EMSubscribeNode *node;
-	GtkTreeIter child;
-	GtkTreeModel *model = (GtkTreeModel *)gtk_tree_view_get_model(tree);
-	gchar *row_name;
-
-	gtk_tree_model_get(model, iter, 1, &row_name, -1);
-	d(printf("%s:%s: row-expanded '%s'\n", G_STRLOC, G_STRFUNC,
-		 row_name?row_name:"<root>"));
-
-	/* Do we really need to fetch the children for this row? */
-	if (gtk_tree_model_iter_n_children(model, iter) > 1) {
-		gtk_tree_model_get(model, iter, 2, &node, -1);
-		if (node->path) {
-			/* Mark it as already-processed path */
-			gtk_tree_path_free(node->path);
-			node->path=NULL;
-		}
-		return;
-	} else {
-		gtk_tree_model_iter_children(model, &child, iter);
-		gtk_tree_model_get(model, &child, 2, &node, -1);
-		if (!node) {
-			/* This is the place holder node, delete it and fire-up a pending */
-			gtk_tree_store_remove((GtkTreeStore *)model, &child);
-			gtk_tree_model_get(model, iter, 2, &node, -1);
-		} else {
-			gtk_tree_model_get(model, iter, 2, &node, -1);
-			if (node->path) {
-				/* Mark it as already-processed path */
-				gtk_tree_path_free(node->path);
-				node->path=NULL;
-			}
-			return;
-		}
-	}
-
-	g_queue_push_head (&sub->pending, node);
-
-	if (sub->pending_id == -1
-	    && (node = g_queue_pop_tail (&sub->pending)) != NULL)
-		sub_queue_fill_level(sub, node);
-}
-
-static void
 sub_destroy(GtkWidget *w, EMSubscribe *sub)
 {
 	struct _zsubscribe_msg *m;
@@ -568,7 +655,6 @@ subscribe_new(EMSubscribeEditor *se, const gchar *uri)
 	sub->editor = se;
 	sub->ref_count = 1;
 	sub->pending_id = -1;
-	g_queue_init (&sub->pending);
 	sub->subscribe_id = -1;
 	g_queue_init (&sub->subscribe);
 	sub->store_id = -1;
@@ -590,9 +676,11 @@ subscribe_set_store(EMSubscribe *sub, CamelStore *store)
 		gtk_widget_show(sub->widget);
 	} else {
 		GtkTreeSelection *selection;
+		GtkTreeViewColumn *column;
 		GtkCellRenderer *renderer;
-		GtkTreeStore *model;
 
+		sub->all_selectable = NULL;
+		sub->tree_expanded_paths = NULL;
 		sub->store = store;
 		g_object_ref (store);
 		sub->folders = g_hash_table_new_full (
@@ -600,9 +688,25 @@ subscribe_set_store(EMSubscribe *sub, CamelStore *store)
 			(GDestroyNotify) NULL,
 			(GDestroyNotify) sub_node_free);
 
-		model = gtk_tree_store_new (3, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
-		sub->tree = (GtkTreeView *) gtk_tree_view_new_with_model ((GtkTreeModel *) model);
-		g_object_unref (model);
+		sub->tree_store = (GtkTreeModel *) gtk_tree_store_new (N_COLUMNS,
+			G_TYPE_BOOLEAN, /* COL_SUBSCRIBED */
+			G_TYPE_STRING,  /* COL_NAME       */
+			G_TYPE_POINTER, /* COL_INFO_NODE  */
+			G_TYPE_BOOLEAN, /* COL_CAN_SELECT */
+			G_TYPE_STRING   /* COL_ICON_NAME  */
+		);
+		g_object_ref_sink (sub->tree_store);
+
+		sub->list_store = (GtkTreeModel *) gtk_list_store_new (N_COLUMNS,
+			G_TYPE_BOOLEAN, /* COL_SUBSCRIBED */
+			G_TYPE_STRING,  /* COL_NAME       */
+			G_TYPE_POINTER, /* COL_INFO_NODE  */
+			G_TYPE_BOOLEAN, /* COL_CAN_SELECT */
+			G_TYPE_STRING   /* COL_ICON_NAME  */
+		);
+		g_object_ref_sink (sub->list_store);
+
+		sub->tree = (GtkTreeView *) gtk_tree_view_new_with_model (sub->editor->is_filtering ? sub->list_store : sub->tree_store);
 		gtk_widget_show ((GtkWidget *)sub->tree);
 
 		sub->widget = gtk_scrolled_window_new (NULL, NULL);
@@ -613,18 +717,31 @@ subscribe_set_store(EMSubscribe *sub, CamelStore *store)
 
 		renderer = gtk_cell_renderer_toggle_new ();
 		g_object_set(renderer, "activatable", TRUE, NULL);
-		gtk_tree_view_insert_column_with_attributes (sub->tree, -1, _("Subscribed"), renderer, "active", 0, NULL);
+		gtk_tree_view_insert_column_with_attributes (sub->tree, -1, _("Subscribed"), renderer, "active", COL_SUBSCRIBED, "visible", COL_CAN_SELECT, NULL);
 		g_signal_connect(renderer, "toggled", G_CALLBACK(sub_subscribe_toggled), sub);
 
+		column = gtk_tree_view_column_new ();
+		gtk_tree_view_column_set_title (column, _("Folder"));
+		gtk_tree_view_append_column (sub->tree, column);
+
+		renderer = gtk_cell_renderer_pixbuf_new ();
+		gtk_tree_view_column_pack_start (column, renderer, FALSE);
+		gtk_tree_view_column_add_attribute (
+			column, renderer, "icon-name", COL_ICON_NAME);
+
 		renderer = gtk_cell_renderer_text_new ();
-		gtk_tree_view_insert_column_with_attributes (sub->tree, -1, _("Folder"), renderer, "text", 1, NULL);
+		gtk_tree_view_column_pack_start (column, renderer, TRUE);
+		gtk_tree_view_column_add_attribute (
+			column, renderer, "text", COL_NAME);
 		gtk_tree_view_set_expander_column(sub->tree, gtk_tree_view_get_column(sub->tree, 1));
 
 		selection = gtk_tree_view_get_selection (sub->tree);
 		gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
 		gtk_tree_view_set_headers_visible (sub->tree, FALSE);
 
-		g_signal_connect(sub->tree, "row-expanded", G_CALLBACK(sub_row_expanded), sub);
+		gtk_tree_view_set_search_column (sub->tree, COL_NAME);
+		gtk_tree_view_set_enable_search (sub->tree, TRUE);
+
 		g_signal_connect(sub->tree, "row-activated", G_CALLBACK(sub_row_activated), sub);
 		g_signal_connect(sub->tree, "destroy", G_CALLBACK(sub_destroy), sub);
 
@@ -632,6 +749,8 @@ subscribe_set_store(EMSubscribe *sub, CamelStore *store)
 		g_signal_connect(selection, "changed", G_CALLBACK(sub_selection_changed), sub);
 
 		sub_queue_fill_level(sub, NULL);
+
+		update_buttons_sesitivity (sub->editor);
 	}
 
 	gtk_box_pack_start((GtkBox *)sub->editor->vbox, sub->widget, TRUE, TRUE, 0);
@@ -644,6 +763,9 @@ sub_editor_destroy(GtkWidget *w, EMSubscribeEditor *se)
 	d(printf("editor destroyed, freeing editor\n"));
 	if (se->busy_id)
 		g_source_remove(se->busy_id);
+	if (se->refilter_id != 0)
+		g_source_remove (se->refilter_id);
+	se->refilter_id = 0;
 
 	g_free(se);
 }
@@ -672,9 +794,15 @@ sub_editor_refresh(GtkWidget *w, EMSubscribeEditor *se)
 		mail_msg_wait(sub->pending_id);
 	}
 
-	gtk_tree_store_clear((GtkTreeStore *)gtk_tree_view_get_model(sub->tree));
+	g_slist_free (sub->all_selectable);
+	sub->all_selectable = NULL;
+
+	g_slist_foreach (sub->tree_expanded_paths, (GFunc) gtk_tree_path_free, NULL);
+	g_slist_free (sub->tree_expanded_paths);
+	sub->tree_expanded_paths = NULL;
 
-	g_queue_init (&sub->pending);
+	gtk_tree_store_clear ((GtkTreeStore *)sub->tree_store);
+	gtk_list_store_clear ((GtkListStore *)sub->list_store);
 
 	if (sub->folders)
 		g_hash_table_destroy(sub->folders);
@@ -714,11 +842,9 @@ sub_editor_combobox_changed (GtkWidget *w, EMSubscribeEditor *se)
 
 	d(printf("combobox changed\n"));
 
-	i = 1;
+	i = 0;
 	n = gtk_combo_box_get_active (GTK_COMBO_BOX (se->combobox));
-	if (n == 0) {
-		gtk_widget_show (se->none_selected);
-	} else {
+	if (n != -1) {
 		GtkTreeIter iter;
 		GtkTreeModel *model;
 
@@ -726,14 +852,27 @@ sub_editor_combobox_changed (GtkWidget *w, EMSubscribeEditor *se)
 
 		model = gtk_combo_box_get_model (GTK_COMBO_BOX (se->combobox));
 		if (gtk_tree_model_get_iter_first (model, &iter)) {
-			/* hide the first item */
-			gtk_list_store_set (
-				GTK_LIST_STORE (model), &iter,
-				1, FALSE,
-				-1);
+			gboolean is_account = TRUE;
+
+			gtk_tree_model_get (model, &iter, 1, &is_account, -1);
+
+			if (!is_account && n > 0) {
+				/* the first node it not an account node, it's the notice
+				   about "select account please", thus remove it completely */
+				gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+				n--;
+			} else if (!is_account) {
+				gtk_widget_show (se->none_selected);
+				i++;
+			}
 		}
 	}
 
+	if (se->refilter_id != 0) {
+		g_source_remove (se->refilter_id);
+		se->refilter_id = 0;
+	}
+
 	se->current = NULL;
 	link = g_queue_peek_head_link (&se->stores);
 	while (link != NULL) {
@@ -755,6 +894,11 @@ sub_editor_combobox_changed (GtkWidget *w, EMSubscribeEditor *se)
 
 		link = g_list_next (link);
 	}
+
+	update_buttons_sesitivity (se);
+
+	if (se->current && se->is_filtering)
+		update_filtering_column (se, se->current);
 }
 
 static gboolean sub_editor_timeout(EMSubscribeEditor *se)
@@ -800,6 +944,137 @@ window_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
 	g_object_unref (gconf);
 }
 
+static void
+store_expanded_rows_cb (GtkTreeView *tree_view, GtkTreePath *path, gpointer data)
+{
+	GSList **slist = data;
+
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (data != NULL);
+
+	*slist = g_slist_prepend (*slist, gtk_tree_path_copy (path));
+}
+
+static void
+expand_to_path_cb (GtkTreePath *path, GtkTreeView *tree_view)
+{
+	g_return_if_fail (path != NULL);
+	g_return_if_fail (tree_view != NULL);
+
+	gtk_tree_view_expand_to_path (tree_view, path);
+}
+
+static void
+change_filtering_models (EMSubscribeEditor *se, gboolean turn_on)
+{
+	GList *link;
+
+	link = g_queue_peek_head_link (&se->stores);
+	while (link != NULL) {
+		struct _EMSubscribe *sub = link->data;
+
+		if (sub->widget && sub->tree) {
+			if (turn_on) {
+				g_slist_foreach (sub->tree_expanded_paths, (GFunc) gtk_tree_path_free, NULL);
+				g_slist_free (sub->tree_expanded_paths);
+				sub->tree_expanded_paths = NULL;
+
+				gtk_tree_view_map_expanded_rows (sub->tree, store_expanded_rows_cb, &sub->tree_expanded_paths);
+
+				gtk_list_store_clear (GTK_LIST_STORE (sub->list_store));
+				gtk_tree_view_set_model (sub->tree, sub->list_store);
+			} else {
+				gtk_tree_view_set_model (sub->tree, sub->tree_store);
+
+				g_slist_foreach (sub->tree_expanded_paths, (GFunc) expand_to_path_cb, sub->tree);
+				g_slist_foreach (sub->tree_expanded_paths, (GFunc) gtk_tree_path_free, NULL);
+				g_slist_free (sub->tree_expanded_paths);
+				sub->tree_expanded_paths = NULL;
+			}
+
+			gtk_tree_view_set_search_column (sub->tree, COL_NAME);
+		}
+
+		link = g_list_next (link);
+	}
+
+	update_buttons_sesitivity (se);
+}
+
+static gboolean
+update_filter_on_timeout_cb (gpointer data)
+{
+	EMSubscribeEditor *se = data;
+
+	g_return_val_if_fail (se != NULL, FALSE);
+
+	se->refilter_id = 0;
+	if (se->current) {
+		/* update filtering options */
+		update_filtering_column (se, se->current);
+	}
+
+	return FALSE;
+}
+
+static void
+clear_filter_cb (GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event, EMSubscribeEditor *se)
+{
+	g_return_if_fail (entry != NULL);
+
+	gtk_entry_set_text (entry, "");
+}
+
+static void
+filter_changed_cb (GtkEntry *entry, EMSubscribeEditor *se)
+{
+	const gchar *text;
+	gboolean was_filtering;
+
+	g_return_if_fail (entry != NULL);
+	g_return_if_fail (se != NULL);
+
+	text = gtk_entry_get_text (entry);
+	was_filtering = se->is_filtering;
+	se->is_filtering = text && *text;
+	gtk_entry_set_icon_sensitive (GTK_ENTRY (se->filter_entry), GTK_ENTRY_ICON_SECONDARY, se->is_filtering);
+
+	if (se->refilter_id != 0) {
+		g_source_remove (se->refilter_id);
+		se->refilter_id = 0;
+	}
+
+	if ((was_filtering && !se->is_filtering) || (!was_filtering && se->is_filtering)) {
+		/* turn on/off filtering - change models */
+		change_filtering_models (se, se->is_filtering);
+	}
+
+	if (se->is_filtering && se->current)
+		se->refilter_id = g_timeout_add (333, update_filter_on_timeout_cb, se);
+}
+
+static void
+expand_all_cb (GtkButton *button, EMSubscribeEditor *se)
+{
+	g_return_if_fail (se != NULL);
+	g_return_if_fail (!se->is_filtering);
+	g_return_if_fail (se->current != NULL);
+	g_return_if_fail (se->current->tree != NULL);
+
+	gtk_tree_view_expand_all (se->current->tree);
+}
+
+static void
+collapse_all_cb (GtkButton *button, EMSubscribeEditor *se)
+{
+	g_return_if_fail (se != NULL);
+	g_return_if_fail (!se->is_filtering);
+	g_return_if_fail (se->current != NULL);
+	g_return_if_fail (se->current->tree != NULL);
+
+	gtk_tree_view_collapse_all (se->current->tree);
+}
+
 GtkWidget *
 em_subscribe_editor_new(void)
 {
@@ -846,11 +1121,22 @@ em_subscribe_editor_new(void)
 	se->progress = e_builder_get_widget(builder, "progress_bar");
 	gtk_widget_hide(se->progress);
 
-	w = e_builder_get_widget(builder, "close_button");
-	g_signal_connect(w, "clicked", G_CALLBACK(sub_editor_close), se);
+	se->filter_entry = e_builder_get_widget (builder, "filter_entry");
+	gtk_entry_set_icon_sensitive (GTK_ENTRY (se->filter_entry), GTK_ENTRY_ICON_SECONDARY, FALSE);
+	g_signal_connect (se->filter_entry, "icon-press", G_CALLBACK (clear_filter_cb), se);
+	g_signal_connect (se->filter_entry, "changed", G_CALLBACK (filter_changed_cb), se);
 
-	w = e_builder_get_widget(builder, "refresh_button");
-	g_signal_connect(w, "clicked", G_CALLBACK(sub_editor_refresh), se);
+	se->expand_button = e_builder_get_widget (builder, "expand_button");
+	g_signal_connect (se->expand_button, "clicked", G_CALLBACK (expand_all_cb), se);
+
+	se->collapse_button = e_builder_get_widget (builder, "collapse_button");
+	g_signal_connect (se->collapse_button, "clicked", G_CALLBACK (collapse_all_cb), se);
+
+	se->refresh_button = e_builder_get_widget (builder, "refresh_button");
+	g_signal_connect (se->refresh_button, "clicked", G_CALLBACK (sub_editor_refresh), se);
+
+	w = e_builder_get_widget (builder, "close_button");
+	g_signal_connect (w, "clicked", G_CALLBACK (sub_editor_close), se);
 
 	/* setup stores combobox */
 	se->combobox = e_builder_get_widget (builder, "store_combobox");
@@ -864,14 +1150,13 @@ em_subscribe_editor_new(void)
 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (se->combobox), cell, TRUE);
 	gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (se->combobox), cell,
                                   "text", 0,
-                                  "visible", 1,
                                   NULL);
 
 	gtk_list_store_append (store, &gtiter);
 	gtk_list_store_set (
 		store, &gtiter,
 		0, _("No server has been selected"),
-		1, TRUE,
+		1, FALSE,
 		-1);
 
 	accounts = e_get_account_list ();
@@ -926,5 +1211,7 @@ em_subscribe_editor_new(void)
 	gtk_window_set_default_size ((GtkWindow *) se->dialog, window_size.width, window_size.height);
 	g_signal_connect (se->dialog, "size-allocate", G_CALLBACK (window_size_allocate), NULL);
 
+	update_buttons_sesitivity (se);
+
 	return GTK_WIDGET (se->dialog);
 }
diff --git a/mail/mail-dialogs.ui b/mail/mail-dialogs.ui
index e416aeb..fa7eda0 100644
--- a/mail/mail-dialogs.ui
+++ b/mail/mail-dialogs.ui
@@ -202,19 +202,26 @@
             <property name="orientation">vertical</property>
             <property name="spacing">12</property>
             <child>
-              <object class="GtkHBox" id="hbox1">
+              <object class="GtkTable" id="top_items_table">
                 <property name="visible">True</property>
-                <property name="spacing">12</property>
+                <property name="n_rows">2</property>
+                <property name="n_columns">3</property>
+                <property name="column_spacing">12</property>
+                <property name="row_spacing">6</property>
                 <child>
                   <object class="GtkLabel" id="label1">
                     <property name="visible">True</property>
                     <property name="label" translatable="yes">S_erver:</property>
                     <property name="use_underline">True</property>
+                    <property name="xalign">1</property>
                   </object>
                   <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="position">0</property>
+                    <property name="left_attach">0</property>
+                    <property name="right_attach">1</property>
+                    <property name="top_attach">0</property>
+                    <property name="bottom_attach">1</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options"></property>
                   </packing>
                 </child>
                 <child>
@@ -222,9 +229,12 @@
                     <property name="visible">True</property>
                   </object>
                   <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="position">1</property>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">2</property>
+                    <property name="top_attach">0</property>
+                    <property name="bottom_attach">1</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options"></property>
                   </packing>
                 </child>
                 <child>
@@ -233,10 +243,40 @@
                     <property name="pulse_step">0.10000000149</property>
                   </object>
                   <packing>
-                    <property name="expand">False</property>
-                    <property name="fill">False</property>
-                    <property name="pack_type">end</property>
-                    <property name="position">2</property>
+                    <property name="left_attach">2</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">0</property>
+                    <property name="bottom_attach">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="filterlabel">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">S_how only items containing:</property>
+                    <property name="use_underline">True</property>
+                    <property name="xalign">1</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">0</property>
+                    <property name="right_attach">1</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="x_options">GTK_FILL</property>
+                    <property name="y_options"></property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkEntry" id="filter_entry">
+                    <property name="visible">True</property>
+                    <property name="secondary_icon_stock">gtk-clear</property>
+                    <property name="secondary_icon_activatable">True</property>
+                  </object>
+                  <packing>
+                    <property name="left_attach">1</property>
+                    <property name="right_attach">3</property>
+                    <property name="top_attach">1</property>
+                    <property name="bottom_attach">2</property>
+                    <property name="y_options"></property>
                   </packing>
                 </child>
               </object>
@@ -268,6 +308,36 @@
             <property name="visible">True</property>
             <property name="layout_style">end</property>
             <child>
+              <object class="GtkButton" id="expand_button">
+                <property name="label" translatable="yes">E_xpand all</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="collapse_button">
+                <property name="label" translatable="yes">Collapse _all</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
               <object class="GtkButton" id="refresh_button">
                 <property name="label">gtk-refresh</property>
                 <property name="visible">True</property>
@@ -279,7 +349,7 @@
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
-                <property name="position">0</property>
+                <property name="position">2</property>
               </packing>
             </child>
             <child>
@@ -294,7 +364,7 @@
               <packing>
                 <property name="expand">False</property>
                 <property name="fill">False</property>
-                <property name="position">1</property>
+                <property name="position">3</property>
               </packing>
             </child>
           </object>



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