[empathy] Use EmpathyLiveSearch in main window's contact list



commit 0cb1c94b09fa663adbabe8b8869a6747524a9b3e
Author: Felix Kaser <f kaser gmx net>
Date:   Tue Jun 8 17:25:12 2010 +0200

    Use EmpathyLiveSearch in main window's contact list

 libempathy-gtk/empathy-contact-list-view.c |  342 +++++++++++++++++++++++++---
 libempathy-gtk/empathy-contact-list-view.h |    3 +
 src/empathy-main-window.c                  |   14 +-
 3 files changed, 321 insertions(+), 38 deletions(-)
---
diff --git a/libempathy-gtk/empathy-contact-list-view.c b/libempathy-gtk/empathy-contact-list-view.c
index 2f5aba1..873fe4a 100644
--- a/libempathy-gtk/empathy-contact-list-view.c
+++ b/libempathy-gtk/empathy-contact-list-view.c
@@ -66,6 +66,9 @@ typedef struct {
 	EmpathyContactFeatureFlags      contact_features;
 	GtkWidget                      *tooltip_widget;
 	GtkTargetList                  *file_targets;
+
+	GtkTreeModelFilter             *filter;
+	GtkWidget                      *search_widget;
 } EmpathyContactListViewPriv;
 
 typedef struct {
@@ -136,6 +139,82 @@ contact_list_view_tooltip_destroy_cb (GtkWidget              *widget,
 }
 
 static gboolean
+contact_list_view_is_visible_contact (EmpathyContactListView *self,
+				      EmpathyContact *contact)
+{
+	EmpathyContactListViewPriv *priv = GET_PRIV (self);
+	EmpathyLiveSearch *live = EMPATHY_LIVE_SEARCH (priv->search_widget);
+	const gchar *str;
+
+	/* check alias name */
+	str = empathy_contact_get_name (contact);
+	if (empathy_live_search_match (live, str))
+		return TRUE;
+
+	/* check contact id */
+	str = empathy_contact_get_id (contact);
+	if (empathy_live_search_match (live, str))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean
+contact_list_view_filter_visible_func (GtkTreeModel *model,
+				       GtkTreeIter  *iter,
+				       gpointer      user_data)
+{
+	EmpathyContactListView     *self = EMPATHY_CONTACT_LIST_VIEW (user_data);
+	EmpathyContactListViewPriv *priv = GET_PRIV (self);
+	EmpathyContact             *contact = NULL;
+	gboolean                    is_group, is_separator, valid;
+	GtkTreeIter                 child_iter;
+	gboolean                    visible;
+
+	if (!gtk_widget_get_visible (priv->search_widget))
+		return TRUE;
+
+	gtk_tree_model_get (model, iter,
+		EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
+		EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator,
+		EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
+		-1);
+
+	if (contact != NULL) {
+		visible = contact_list_view_is_visible_contact (self, contact);
+		g_object_unref (contact);
+		return visible;
+	}
+
+	if (is_separator) {
+		return TRUE;
+	}
+
+	/* Not a contact, not a separator, must be a group */
+	g_return_val_if_fail (is_group, FALSE);
+
+	/* only show groups which are not empty */
+	for (valid = gtk_tree_model_iter_children (model, &child_iter, iter);
+	     valid; valid = gtk_tree_model_iter_next (model, &child_iter)) {
+		gtk_tree_model_get (model, &child_iter,
+			EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
+			-1);
+
+		if (contact == NULL)
+			continue;
+
+		visible = contact_list_view_is_visible_contact (self, contact);
+		g_object_unref (contact);
+
+		/* show group if it has at least one visible contact in it */
+		if (visible)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+static gboolean
 contact_list_view_query_tooltip_cb (EmpathyContactListView *view,
 				    gint                    x,
 				    gint                    y,
@@ -791,7 +870,7 @@ contact_list_view_row_activated (GtkTreeView       *view,
 		return;
 	}
 
-	model = GTK_TREE_MODEL (priv->store);
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
 	gtk_tree_model_get_iter (model, &iter, path);
 	gtk_tree_model_get (model, &iter,
 			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
@@ -1096,6 +1175,142 @@ contact_list_view_row_expand_or_collapse_cb (EmpathyContactListView *view,
 	g_free (name);
 }
 
+static gboolean
+contact_list_view_start_search_cb (EmpathyContactListView *view,
+				   gpointer                data)
+{
+	EmpathyContactListViewPriv *priv = GET_PRIV (view);
+
+	if (priv->search_widget == NULL)
+		return FALSE;
+
+	if (gtk_widget_get_visible (GTK_WIDGET (priv->search_widget)))
+		gtk_widget_grab_focus (GTK_WIDGET (priv->search_widget));
+	else
+		gtk_widget_show (GTK_WIDGET (priv->search_widget));
+
+	return TRUE;
+}
+
+static void
+contact_list_view_search_text_notify_cb (EmpathyLiveSearch      *search,
+					 GParamSpec             *pspec,
+					 EmpathyContactListView *view)
+{
+	EmpathyContactListViewPriv *priv = GET_PRIV (view);
+
+	gtk_tree_model_filter_refilter (priv->filter);
+}
+
+static void
+contact_list_view_search_hide_cb (EmpathyLiveSearch      *search,
+				  EmpathyContactListView *view)
+{
+	EmpathyContactListViewPriv *priv = GET_PRIV (view);
+	GtkTreeModel               *model;
+	GtkTreeIter                 iter;
+	gboolean                    valid = FALSE;
+
+	/* block expand or collapse handlers, they would write the
+	 * expand or collapsed setting to file otherwise */
+	g_signal_handlers_block_by_func (view,
+		contact_list_view_row_expand_or_collapse_cb,
+		GINT_TO_POINTER (TRUE));
+	g_signal_handlers_block_by_func (view,
+		contact_list_view_row_expand_or_collapse_cb,
+		GINT_TO_POINTER (FALSE));
+
+	/* restore which groups are expanded and which are not */
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
+	for (valid = gtk_tree_model_get_iter_first (model, &iter);
+	     valid; valid = gtk_tree_model_iter_next (model, &iter)) {
+		gboolean      is_group;
+		gchar        *name = NULL;
+		GtkTreePath  *path;
+
+		gtk_tree_model_get (model, &iter,
+			EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
+			EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
+			-1);
+
+		if (!is_group) {
+			g_free (name);
+			continue;
+		}
+
+		path = gtk_tree_model_get_path (model, &iter);
+		if ((priv->list_features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE) == 0 ||
+		    empathy_contact_group_get_expanded (name)) {
+			gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path,
+				TRUE);
+		} else {
+			gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), path);
+		}
+
+		gtk_tree_path_free(path);
+		g_free (name);
+	}
+
+	/* unblock expand or collapse handlers */
+	g_signal_handlers_unblock_by_func (view,
+		contact_list_view_row_expand_or_collapse_cb,
+		GINT_TO_POINTER (TRUE));
+	g_signal_handlers_unblock_by_func (view,
+		contact_list_view_row_expand_or_collapse_cb,
+		GINT_TO_POINTER (FALSE));
+}
+
+static void
+contact_list_view_search_show_cb (EmpathyLiveSearch      *search,
+				  EmpathyContactListView *view)
+{
+	/* block expand or collapse handlers during expand all, they would
+	 * write the expand or collapsed setting to file otherwise */
+	g_signal_handlers_block_by_func (view,
+		contact_list_view_row_expand_or_collapse_cb,
+		GINT_TO_POINTER (TRUE));
+
+	gtk_tree_view_expand_all (GTK_TREE_VIEW (view));
+
+	g_signal_handlers_unblock_by_func (view,
+		contact_list_view_row_expand_or_collapse_cb,
+		GINT_TO_POINTER (TRUE));
+}
+
+typedef struct {
+	EmpathyContactListView *view;
+	GtkTreePath *path;
+	gboolean expand;
+} ExpandData;
+
+static gboolean
+contact_list_view_expand_idle_cb (gpointer user_data)
+{
+	ExpandData *data = user_data;
+
+	g_signal_handlers_block_by_func (data->view,
+		contact_list_view_row_expand_or_collapse_cb,
+		GINT_TO_POINTER (data->expand));
+
+	if (data->expand) {
+		gtk_tree_view_expand_row (GTK_TREE_VIEW (data->view),
+			data->path, TRUE);
+	} else {
+		gtk_tree_view_collapse_row (GTK_TREE_VIEW (data->view),
+			data->path);
+	}
+
+	g_signal_handlers_unblock_by_func (data->view,
+		contact_list_view_row_expand_or_collapse_cb,
+		GINT_TO_POINTER (data->expand));
+
+	g_object_unref (data->view);
+	gtk_tree_path_free (data->path);
+	g_slice_free (ExpandData, data);
+
+	return FALSE;
+}
+
 static void
 contact_list_view_row_has_child_toggled_cb (GtkTreeModel           *model,
 					    GtkTreePath            *path,
@@ -1105,6 +1320,7 @@ contact_list_view_row_has_child_toggled_cb (GtkTreeModel           *model,
 	EmpathyContactListViewPriv *priv = GET_PRIV (view);
 	gboolean  is_group = FALSE;
 	gchar    *name = NULL;
+	ExpandData *data;
 
 	gtk_tree_model_get (model, iter,
 			    EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
@@ -1116,47 +1332,46 @@ contact_list_view_row_has_child_toggled_cb (GtkTreeModel           *model,
 		return;
 	}
 
-	if (!(priv->list_features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE) ||
-	    empathy_contact_group_get_expanded (name)) {
-		g_signal_handlers_block_by_func (view,
-						 contact_list_view_row_expand_or_collapse_cb,
-						 GINT_TO_POINTER (TRUE));
-		gtk_tree_view_expand_row (GTK_TREE_VIEW (view), path, TRUE);
-		g_signal_handlers_unblock_by_func (view,
-						   contact_list_view_row_expand_or_collapse_cb,
-						   GINT_TO_POINTER (TRUE));
-	} else {
-		g_signal_handlers_block_by_func (view,
-						 contact_list_view_row_expand_or_collapse_cb,
-						 GINT_TO_POINTER (FALSE));
-		gtk_tree_view_collapse_row (GTK_TREE_VIEW (view), path);
-		g_signal_handlers_unblock_by_func (view,
-						   contact_list_view_row_expand_or_collapse_cb,
-						   GINT_TO_POINTER (FALSE));
-	}
+	data = g_slice_new0 (ExpandData);
+	data->view = g_object_ref (view);
+	data->path = gtk_tree_path_copy (path);
+	data->expand =
+		(priv->list_features & EMPATHY_CONTACT_LIST_FEATURE_GROUPS_SAVE) == 0 ||
+		(priv->search_widget != NULL && gtk_widget_get_visible (priv->search_widget)) ||
+		empathy_contact_group_get_expanded (name);
+
+	/* FIXME: It doesn't work to call gtk_tree_view_expand_row() from within
+	 * gtk_tree_model_filter_refilter() */
+	g_idle_add (contact_list_view_expand_idle_cb, data);
 
 	g_free (name);
 }
 
 static void
-contact_list_view_setup (EmpathyContactListView *view)
+contact_list_view_constructed (GObject *object)
 {
-	EmpathyContactListViewPriv *priv;
-	GtkCellRenderer           *cell;
-	GtkTreeViewColumn         *col;
-	guint                      i;
-
-	priv = GET_PRIV (view);
+	EmpathyContactListView     *view = EMPATHY_CONTACT_LIST_VIEW (object);
+	EmpathyContactListViewPriv *priv = GET_PRIV (view);
+	GtkCellRenderer            *cell;
+	GtkTreeViewColumn          *col;
+	guint                       i;
 
 	gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (view),
 					     empathy_contact_list_store_search_equal_func,
 					     NULL, NULL);
 
-	g_signal_connect (priv->store, "row-has-child-toggled",
+	priv->filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (
+			GTK_TREE_MODEL (priv->store), NULL));
+	gtk_tree_model_filter_set_visible_func (priv->filter,
+			contact_list_view_filter_visible_func,
+			view, NULL);
+
+	g_signal_connect (priv->filter, "row-has-child-toggled",
 			  G_CALLBACK (contact_list_view_row_has_child_toggled_cb),
 			  view);
+
 	gtk_tree_view_set_model (GTK_TREE_VIEW (view),
-				 GTK_TREE_MODEL (priv->store));
+				 GTK_TREE_MODEL (priv->filter));
 
 	/* Setup view */
 	/* Setting reorderable is a hack that gets us row previews as drag icons
@@ -1318,23 +1533,31 @@ contact_list_view_set_list_features (EmpathyContactListView         *view,
 }
 
 static void
-contact_list_view_finalize (GObject *object)
+contact_list_view_dispose (GObject *object)
 {
-	EmpathyContactListViewPriv *priv;
-
-	priv = GET_PRIV (object);
+	EmpathyContactListView *view = EMPATHY_CONTACT_LIST_VIEW (object);
+	EmpathyContactListViewPriv *priv = GET_PRIV (view);
 
 	if (priv->store) {
 		g_object_unref (priv->store);
+		priv->store = NULL;
+	}
+	if (priv->filter) {
+		g_object_unref (priv->filter);
+		priv->filter = NULL;
 	}
 	if (priv->tooltip_widget) {
 		gtk_widget_destroy (priv->tooltip_widget);
+		priv->tooltip_widget = NULL;
 	}
 	if (priv->file_targets) {
 		gtk_target_list_unref (priv->file_targets);
+		priv->file_targets = NULL;
 	}
 
-	G_OBJECT_CLASS (empathy_contact_list_view_parent_class)->finalize (object);
+	empathy_contact_list_view_set_live_search (view, NULL);
+
+	G_OBJECT_CLASS (empathy_contact_list_view_parent_class)->dispose (object);
 }
 
 static void
@@ -1375,7 +1598,6 @@ contact_list_view_set_property (GObject      *object,
 	switch (param_id) {
 	case PROP_STORE:
 		priv->store = g_value_dup_object (value);
-		contact_list_view_setup (view);
 		break;
 	case PROP_LIST_FEATURES:
 		contact_list_view_set_list_features (view, g_value_get_flags (value));
@@ -1396,9 +1618,10 @@ empathy_contact_list_view_class_init (EmpathyContactListViewClass *klass)
 	GtkWidgetClass   *widget_class = GTK_WIDGET_CLASS (klass);
 	GtkTreeViewClass *tree_view_class = GTK_TREE_VIEW_CLASS (klass);
 
-	object_class->finalize = contact_list_view_finalize;
-	object_class->get_property = contact_list_view_get_property;
-	object_class->set_property = contact_list_view_set_property;
+	object_class->constructed        = contact_list_view_constructed;
+	object_class->dispose            = contact_list_view_dispose;
+	object_class->get_property       = contact_list_view_get_property;
+	object_class->set_property       = contact_list_view_set_property;
 
 	widget_class->drag_data_received = contact_list_view_drag_data_received;
 	widget_class->drag_drop          = contact_list_view_drag_drop;
@@ -1456,6 +1679,7 @@ empathy_contact_list_view_init (EmpathyContactListView *view)
 		EMPATHY_TYPE_CONTACT_LIST_VIEW, EmpathyContactListViewPriv);
 
 	view->priv = priv;
+
 	/* Get saved group states. */
 	empathy_contact_groups_get_all ();
 
@@ -1769,3 +1993,47 @@ empathy_contact_list_view_get_contact_menu (EmpathyContactListView *view)
 	return menu;
 }
 
+void
+empathy_contact_list_view_set_live_search (EmpathyContactListView *view,
+					   EmpathyLiveSearch      *search)
+{
+	EmpathyContactListViewPriv *priv = GET_PRIV (view);
+
+	/* remove old handlers if old search was not null */
+	if (priv->search_widget != NULL) {
+		g_signal_handlers_disconnect_by_func (view,
+			contact_list_view_start_search_cb,
+			NULL);
+
+		g_signal_handlers_disconnect_by_func (priv->search_widget,
+			contact_list_view_search_text_notify_cb,
+			view);
+		g_signal_handlers_disconnect_by_func (priv->search_widget,
+			contact_list_view_search_hide_cb,
+			view);
+		g_signal_handlers_disconnect_by_func (priv->search_widget,
+			contact_list_view_search_show_cb,
+			view);
+		g_object_unref (priv->search_widget);
+		priv->search_widget = NULL;
+	}
+
+	/* connect handlers if new search is not null */
+	if (search != NULL) {
+		priv->search_widget = g_object_ref (search);
+
+		g_signal_connect (view, "start-interactive-search",
+				  G_CALLBACK (contact_list_view_start_search_cb),
+				  NULL);
+
+		g_signal_connect (priv->search_widget, "notify::text",
+			G_CALLBACK (contact_list_view_search_text_notify_cb),
+			view);
+		g_signal_connect (priv->search_widget, "hide",
+			G_CALLBACK (contact_list_view_search_hide_cb),
+			view);
+		g_signal_connect (priv->search_widget, "show",
+			G_CALLBACK (contact_list_view_search_show_cb),
+			view);
+	}
+}
diff --git a/libempathy-gtk/empathy-contact-list-view.h b/libempathy-gtk/empathy-contact-list-view.h
index 41b968d..97e78d3 100644
--- a/libempathy-gtk/empathy-contact-list-view.h
+++ b/libempathy-gtk/empathy-contact-list-view.h
@@ -32,6 +32,7 @@
 #include <libempathy/empathy-enum-types.h>
 
 #include "empathy-contact-list-store.h"
+#include "empathy-live-search.h"
 #include "empathy-contact-menu.h"
 
 G_BEGIN_DECLS
@@ -77,6 +78,8 @@ gchar *                    empathy_contact_list_view_get_selected_group (Empathy
 									 gboolean *is_fake_group);
 GtkWidget *                empathy_contact_list_view_get_contact_menu   (EmpathyContactListView         *view);
 GtkWidget *                empathy_contact_list_view_get_group_menu     (EmpathyContactListView         *view);
+void                       empathy_contact_list_view_set_live_search    (EmpathyContactListView         *view,
+								         EmpathyLiveSearch              *search);
 
 G_END_DECLS
 
diff --git a/src/empathy-main-window.c b/src/empathy-main-window.c
index bf397de..998c024 100644
--- a/src/empathy-main-window.c
+++ b/src/empathy-main-window.c
@@ -44,6 +44,7 @@
 #include <libempathy-gtk/empathy-contact-dialogs.h>
 #include <libempathy-gtk/empathy-contact-list-store.h>
 #include <libempathy-gtk/empathy-contact-list-view.h>
+#include <libempathy-gtk/empathy-live-search.h>
 #include <libempathy-gtk/empathy-geometry.h>
 #include <libempathy-gtk/empathy-gtk-enum-types.h>
 #include <libempathy-gtk/empathy-new-message-dialog.h>
@@ -97,6 +98,7 @@ typedef struct {
 	GtkWidget              *presence_toolbar;
 	GtkWidget              *presence_chooser;
 	GtkWidget              *errors_vbox;
+	GtkWidget              *search_bar;
 
 	GtkToggleAction        *show_protocols;
 	GtkRadioAction         *sort_by_name;
@@ -292,7 +294,7 @@ main_window_row_activated_cb (EmpathyContactListView *view,
 	GtkTreeIter     iter;
 	GSList         *events, *l;
 
-	model = GTK_TREE_MODEL (window->list_store);
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (window->list_view));
 	gtk_tree_model_get_iter (model, &iter, path);
 	gtk_tree_model_get (model, &iter,
 			    EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
@@ -1516,9 +1518,11 @@ empathy_main_window_show (void)
 	window->list_view = empathy_contact_list_view_new (window->list_store,
 							   EMPATHY_CONTACT_LIST_FEATURE_ALL,
 							   EMPATHY_CONTACT_FEATURE_ALL);
+
 	window->butterfly_log_migration_members_changed_id = g_signal_connect (
 		list_iface, "members-changed",
 		G_CALLBACK (main_window_members_changed_cb), window);
+
 	g_object_unref (list_iface);
 
 	gtk_widget_show (GTK_WIDGET (window->list_view));
@@ -1528,6 +1532,14 @@ empathy_main_window_show (void)
 			  G_CALLBACK (main_window_row_activated_cb),
 			  window);
 
+	/* Set up search bar */
+	window->search_bar = empathy_live_search_new (
+		GTK_WIDGET (window->list_view));
+	empathy_contact_list_view_set_live_search (window->list_view,
+		EMPATHY_LIVE_SEARCH (window->search_bar));
+	gtk_box_pack_start (GTK_BOX (window->main_vbox), window->search_bar,
+		FALSE, TRUE, 0);
+
 	/* Load user-defined accelerators. */
 	main_window_accels_load ();
 



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