]> git.0d.be Git - empathy.git/blobdiff - libempathy-gtk/empathy-contact-selector.c
Merge branch 'sasl'
[empathy.git] / libempathy-gtk / empathy-contact-selector.c
index f7c42e5ce5dd1ac950917bfb7120c6c7b2017b21..c70d2575acc66b991dd88a0d7e77eb7ac8783dd2 100644 (file)
 
 #include <libempathy/empathy-contact.h>
 #include <libempathy-gtk/empathy-contact-list-store.h>
+#include <libempathy/empathy-utils.h>
+
 #include "empathy-contact-selector.h"
 
+/**
+ * SECTION:empathy-contact-selector
+ * @title:EmpathyContactSelector
+ * @short_description: A widget used to choose from a list of contacts.
+ * @include: libempathy-gtk/empathy-contact-selector.h
+ *
+ * #EmpathyContactSelector is a widget which extends #GtkComboBox to provide
+ * a chooser of available contacts.
+ */
+
+/**
+ * EmpathyContactSelector:
+ * @parent: parent object
+ *
+ * Widget which extends #GtkComboBox to provide a chooser of available contacts.
+ */
+
 G_DEFINE_TYPE (EmpathyContactSelector, empathy_contact_selector,
     GTK_TYPE_COMBO_BOX)
 
-#define GET_PRIV(object) (G_TYPE_INSTANCE_GET_PRIVATE \
-    ((object), EMPATHY_TYPE_CONTACT_SELECTOR, EmpathyContactSelectorPriv))
-
 enum
 {
   PROP_0,
-  PROP_STORE
+  PROP_CONTACT_LIST
 };
 
-typedef struct _EmpathyContactSelectorPriv EmpathyContactSelectorPriv;
-
-struct _EmpathyContactSelectorPriv
+#define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContactSelector)
+typedef struct
 {
+  EmpathyContactList *contact_list;
   EmpathyContactListStore *store;
-  gboolean is_blank_set;
-};
+  GtkTreeModel *model;
+  gboolean dispose_run;
+} EmpathyContactSelectorPriv;
 
-static void changed_cb (GtkComboBox *widget, gpointer data);
-static gboolean get_iter_for_contact (GtkTreeStore *store,
-    GtkTreeIter *list_iter, EmpathyContact *contact);
+static void contact_selector_manage_blank_contact (
+    EmpathyContactSelector *selector);
 
-
-EmpathyContact *
-empathy_contact_selector_get_selected (EmpathyContactSelector *selector)
+static guint
+contact_selector_get_number_online_contacts (GtkTreeModel *model)
 {
-  EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
-  EmpathyContact *contact = NULL;
-  GtkTreeIter iter;
-
-  if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (selector), &iter))
-    return NULL;
+  GtkTreeIter tmp_iter;
+  gboolean is_online;
+  guint number_online_contacts = 0;
+  gboolean ok;
 
-  gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter,
-      EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, -1);
+  for (ok = gtk_tree_model_get_iter_first (model, &tmp_iter);
+      ok; ok = gtk_tree_model_iter_next (model, &tmp_iter))
+    {
+      gtk_tree_model_get (model,
+          &tmp_iter, EMPATHY_CONTACT_LIST_STORE_COL_IS_ONLINE,
+          &is_online, -1);
+      if (is_online)
+        number_online_contacts++;
+    }
 
-  return contact;
+  return number_online_contacts;
 }
 
+static gboolean
+contact_selector_get_iter_for_blank_contact (GtkTreeStore *model,
+                                             GtkTreeIter *blank_iter)
+{
+  GtkTreeIter tmp_iter;
+  EmpathyContact *tmp_contact;
+  gboolean is_present = FALSE;
+  gboolean ok;
+
+  for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &tmp_iter);
+      ok; ok = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &tmp_iter))
+    {
+      gtk_tree_model_get (GTK_TREE_MODEL (model),
+          &tmp_iter, EMPATHY_CONTACT_LIST_STORE_COL_CONTACT,
+          &tmp_contact, -1);
+      if (tmp_contact == NULL)
+        {
+          *blank_iter = tmp_iter;
+          is_present = TRUE;
+          break;
+        }
+      g_object_unref (tmp_contact);
+    }
+
+  return is_present;
+}
 
 static void
-set_blank_contact (EmpathyContactSelector *selector)
+contact_selector_add_blank_contact (EmpathyContactSelector *selector)
 {
   EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
-  GtkTreeIter blank_iter;
+  GtkTreeIter blank_iter, iter;
 
-  gtk_tree_store_insert (GTK_TREE_STORE (priv->store), &blank_iter, NULL, 0);
-  gtk_tree_store_set (GTK_TREE_STORE (priv->store), &blank_iter,
+  gtk_tree_store_insert_with_values (
+      GTK_TREE_STORE (priv->store), &blank_iter, NULL, 0,
       EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, NULL,
-      EMPATHY_CONTACT_LIST_STORE_COL_NAME, ("Select a contact"), -1);
-  g_signal_handlers_block_by_func(selector, changed_cb, NULL);
-  gtk_combo_box_set_active_iter (GTK_COMBO_BOX (selector), &blank_iter);
-  g_signal_handlers_unblock_by_func(selector, changed_cb, NULL);
-  priv->is_blank_set = TRUE;
+      EMPATHY_CONTACT_LIST_STORE_COL_NAME, (_("Select a contact")),
+      EMPATHY_CONTACT_LIST_STORE_COL_IS_ONLINE, FALSE, -1);
+
+  /* look up blank_iter in the filter model */
+  g_return_if_fail (gtk_tree_model_filter_convert_child_iter_to_iter (
+      GTK_TREE_MODEL_FILTER (priv->model), &iter, &blank_iter));
+
+  g_signal_handlers_block_by_func (selector,
+      contact_selector_manage_blank_contact, selector);
+  gtk_combo_box_set_active_iter (GTK_COMBO_BOX (selector), &iter);
+  g_signal_handlers_unblock_by_func (selector,
+      contact_selector_manage_blank_contact, selector);
 }
 
-
 static void
-notify_popup_shown_cb (GtkComboBox *widget,
-                       GParamSpec *property,
-                       gpointer data)
+contact_selector_remove_blank_contact (EmpathyContactSelector *selector)
 {
-  EmpathyContactSelector *selector = EMPATHY_CONTACT_SELECTOR (widget);
   EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
   GtkTreeIter blank_iter;
-  gboolean shown;
-
-  g_object_get (widget, property->name, &shown, NULL);
 
-  if (shown)
-    {
-      if (get_iter_for_contact (GTK_TREE_STORE (priv->store), &blank_iter, NULL))
-        {
-          gtk_tree_store_remove (GTK_TREE_STORE (priv->store), &blank_iter);
-          priv->is_blank_set = FALSE;
-        }
-    }
-  else
-    {
-      if (gtk_combo_box_get_active (widget) == -1)
-        {
-          set_blank_contact (selector);
-          if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->store),
-              NULL) == 1)
-          gtk_widget_set_sensitive (GTK_WIDGET (selector), FALSE);
-        }
-    }
+  if (contact_selector_get_iter_for_blank_contact
+      (GTK_TREE_STORE (priv->store), &blank_iter))
+    gtk_tree_store_remove (GTK_TREE_STORE (priv->store), &blank_iter);
 }
 
-
 static void
-changed_cb (GtkComboBox *widget,
-            gpointer data)
+contact_selector_manage_sensitivity (EmpathyContactSelector *selector)
 {
-  EmpathyContactSelector *selector = EMPATHY_CONTACT_SELECTOR (widget);
   EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
-  GtkTreeIter blank_iter;
-  gboolean shown;
+  guint number_online_contacts;
 
-  g_object_get (widget, "popup-shown", &shown, NULL);
+  number_online_contacts = contact_selector_get_number_online_contacts (priv->model);
 
-  if (shown)
-    return;
+  if (number_online_contacts != 0)
+    gtk_widget_set_sensitive (GTK_WIDGET (selector), TRUE);
+  else
+    gtk_widget_set_sensitive (GTK_WIDGET (selector), FALSE);
+}
+
+static void
+contact_selector_manage_blank_contact (EmpathyContactSelector *selector)
+{
+  gboolean is_popup_shown;
 
-  if (gtk_combo_box_get_active (widget) == -1)
+  g_object_get (selector, "popup-shown", &is_popup_shown, NULL);
+
+  if (is_popup_shown)
     {
-      set_blank_contact (selector);
-      if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->store),
-        NULL) == 1)
-        gtk_widget_set_sensitive (GTK_WIDGET (selector), FALSE);
+      contact_selector_remove_blank_contact (selector);
     }
   else
     {
-      if (get_iter_for_contact (GTK_TREE_STORE (priv->store), &blank_iter, NULL))
+      if (gtk_combo_box_get_active (GTK_COMBO_BOX (selector)) == -1)
         {
-          gtk_tree_store_remove (GTK_TREE_STORE (priv->store), &blank_iter);
-          priv->is_blank_set = FALSE;
+          contact_selector_add_blank_contact (selector);
         }
-    }
-}
-
-
-static gboolean
-get_iter_for_contact (GtkTreeStore *store,
-                      GtkTreeIter *list_iter,
-                      EmpathyContact *contact)
-{
-  GtkTreePath *path;
-  GtkTreeIter tmp_iter;
-  EmpathyContact *tmp_contact;
-  gboolean found = FALSE;
-
-  /* Do a linear search to find the row with CONTACT_COL set to contact. */
-  path = gtk_tree_path_new_first ();
-  if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &tmp_iter, path))
-    {
-      do
+      else
         {
-          gtk_tree_model_get (GTK_TREE_MODEL (store),
-              &tmp_iter, EMPATHY_CONTACT_LIST_STORE_COL_CONTACT,
-              &tmp_contact, -1);
-          found = (tmp_contact == contact);
-          if (found)
-            {
-              *list_iter = tmp_iter;
-              break;
-            }
-        } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store),
-              &tmp_iter));
+          contact_selector_remove_blank_contact (selector);
+        }
     }
 
-  gtk_tree_path_free (path);
-  return found;
+  contact_selector_manage_sensitivity (selector);
 }
 
-
-static void
-empathy_store_row_changed_cb (EmpathyContactListStore *empathy_store,
-                              GtkTreePath *empathy_path,
-                              GtkTreeIter *empathy_iter,
-                              gpointer data)
-{
-  EmpathyContactSelector *selector = EMPATHY_CONTACT_SELECTOR (data);
-  EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
-  gint children;
-
-  children = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->store), NULL);
-
-  if (children == 1 && priv->is_blank_set)
-      gtk_widget_set_sensitive (GTK_WIDGET (selector), FALSE);
-  else if (children)
-      gtk_widget_set_sensitive (GTK_WIDGET (selector), TRUE);
-  else
-      gtk_widget_set_sensitive (GTK_WIDGET (selector), FALSE);
-}
-
-
 static GObject *
-empathy_contact_selector_constructor (GType type,
-                                      guint n_construct_params,
-                                      GObjectConstructParam *construct_params)
+contact_selector_constructor (GType type,
+                              guint n_construct_params,
+                              GObjectConstructParam *construct_params)
 {
-  GObject *object =
-      G_OBJECT_CLASS (empathy_contact_selector_parent_class)->constructor (type,
-      n_construct_params, construct_params);
-  EmpathyContactSelector *contact_selector = EMPATHY_CONTACT_SELECTOR (object);
-  EmpathyContactSelectorPriv *priv = GET_PRIV (contact_selector);
+  GObject *object;
+  EmpathyContactSelector *contact_selector;
+  EmpathyContactSelectorPriv *priv;
+  GtkCellLayout *cell_layout;
   GtkCellRenderer *renderer;
 
+  object = G_OBJECT_CLASS (empathy_contact_selector_parent_class)->constructor
+    (type, n_construct_params, construct_params);
+  priv = GET_PRIV (object);
+  contact_selector = EMPATHY_CONTACT_SELECTOR (object);
+  cell_layout = GTK_CELL_LAYOUT (object);
+
+  priv->store = empathy_contact_list_store_new (priv->contact_list);
+
   g_object_set (priv->store, "is-compact", TRUE, "show-avatars", FALSE,
-      "show-offline", FALSE, "show-groups", FALSE,
+      "show-offline", FALSE, "show-groups", FALSE, "show-protocols", FALSE,
       "sort-criterium", EMPATHY_CONTACT_LIST_STORE_SORT_NAME, NULL);
 
-  g_signal_connect (priv->store, "row-changed",
-      G_CALLBACK (empathy_store_row_changed_cb), (gpointer) contact_selector);
-  g_signal_connect (GTK_COMBO_BOX (contact_selector), "changed",
-      G_CALLBACK (changed_cb), NULL);
-  g_signal_connect (GTK_COMBO_BOX (contact_selector), "notify::popup-shown",
-      G_CALLBACK (notify_popup_shown_cb), NULL);
+  g_signal_connect_swapped (priv->store, "row-changed",
+      G_CALLBACK (contact_selector_manage_sensitivity),
+      contact_selector);
+  g_signal_connect_swapped (contact_selector, "changed",
+      G_CALLBACK (contact_selector_manage_blank_contact),
+      contact_selector);
+  g_signal_connect_swapped (contact_selector, "notify::popup-shown",
+      G_CALLBACK (contact_selector_manage_blank_contact),
+      contact_selector);
 
-  gtk_combo_box_set_model (GTK_COMBO_BOX (contact_selector),
-      GTK_TREE_MODEL (priv->store));
+  priv->model = gtk_tree_model_filter_new (GTK_TREE_MODEL (priv->store), NULL);
+
+  gtk_combo_box_set_model (GTK_COMBO_BOX (contact_selector), priv->model);
   gtk_widget_set_sensitive (GTK_WIDGET (contact_selector), FALSE);
 
-  /* Status icon */
   renderer = gtk_cell_renderer_pixbuf_new ();
-  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (contact_selector),
-      renderer, FALSE);
-  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (contact_selector), renderer,
-      "icon-name", EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, NULL);
+  gtk_cell_layout_pack_start (cell_layout, renderer, FALSE);
+  gtk_cell_layout_set_attributes (cell_layout, renderer,
+      "pixbuf", EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, NULL);
 
-  /* Contact name */
   renderer = gtk_cell_renderer_text_new ();
-  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (contact_selector),
-      renderer, TRUE);
-  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (contact_selector), renderer,
+  gtk_cell_layout_pack_start (cell_layout, renderer, TRUE);
+  gtk_cell_layout_set_attributes (cell_layout, renderer,
       "text", EMPATHY_CONTACT_LIST_STORE_COL_NAME, NULL);
 
-  set_blank_contact (contact_selector);
+  contact_selector_manage_blank_contact (contact_selector);
+  contact_selector_manage_sensitivity (contact_selector);
 
-  object = G_OBJECT (contact_selector);
   return object;
 }
 
-
 static void
 empathy_contact_selector_init (EmpathyContactSelector *empathy_contact_selector)
 {
-}
+  EmpathyContactSelectorPriv *priv =
+      G_TYPE_INSTANCE_GET_PRIVATE (empathy_contact_selector,
+      EMPATHY_TYPE_CONTACT_SELECTOR, EmpathyContactSelectorPriv);
 
+  empathy_contact_selector->priv = priv;
+
+  priv->dispose_run = FALSE;
+}
 
 static void
-empathy_contact_selector_set_property (GObject *object,
-                                       guint prop_id,
-                                       const GValue *value,
-                                       GParamSpec *pspec)
+contact_selector_set_property (GObject *object,
+                               guint prop_id,
+                               const GValue *value,
+                               GParamSpec *pspec)
 {
-  EmpathyContactSelector *contact_selector = EMPATHY_CONTACT_SELECTOR (object);
-  EmpathyContactSelectorPriv *priv = GET_PRIV (contact_selector);
+  EmpathyContactSelectorPriv *priv = GET_PRIV (object);
 
   switch (prop_id)
     {
-      case PROP_STORE:
-        priv->store = g_value_dup_object (value);
+      case PROP_CONTACT_LIST:
+        priv->contact_list = g_value_dup_object (value);
         break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -282,20 +276,18 @@ empathy_contact_selector_set_property (GObject *object,
     }
 }
 
-
 static void
-empathy_contact_selector_get_property (GObject *object,
-                                       guint prop_id,
-                                       GValue *value,
-                                       GParamSpec *pspec)
+contact_selector_get_property (GObject *object,
+                               guint prop_id,
+                               GValue *value,
+                               GParamSpec *pspec)
 {
-  EmpathyContactSelector *contact_selector = EMPATHY_CONTACT_SELECTOR (object);
-  EmpathyContactSelectorPriv *priv = GET_PRIV (contact_selector);
+  EmpathyContactSelectorPriv *priv = GET_PRIV (object);
 
   switch (prop_id)
     {
-      case PROP_STORE:
-        g_value_set_object (value, priv->store);
+      case PROP_CONTACT_LIST:
+        g_value_set_object (value, priv->contact_list);
         break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -303,45 +295,179 @@ empathy_contact_selector_get_property (GObject *object,
     }
 }
 
-
 static void
-empathy_contact_selector_dispose (GObject *object)
+contact_selector_dispose (GObject *object)
 {
-  g_debug ("EmpathyContactSelector - dispose: %p",  object);
+  EmpathyContactSelector *selector = EMPATHY_CONTACT_SELECTOR (object);
+  EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
 
-  (G_OBJECT_CLASS (empathy_contact_selector_parent_class)->dispose) (object);
-}
+  if (priv->dispose_run)
+    return;
 
+  priv->dispose_run = TRUE;
 
-static void
-empathy_contact_selector_finalize (GObject *object)
-{
-  g_debug ("EmpathyContactSelector - finalize: %p",  object);
+  if (priv->contact_list)
+    {
+      g_object_unref (priv->contact_list);
+      priv->contact_list = NULL;
+    }
 
-  (G_OBJECT_CLASS (empathy_contact_selector_parent_class)->finalize) (object);
-}
+  if (priv->model)
+    {
+      g_object_unref (priv->model);
+      priv->model = NULL;
+    }
 
+  if (priv->store)
+    {
+      g_object_unref (priv->store);
+      priv->store = NULL;
+    }
+
+  (G_OBJECT_CLASS (empathy_contact_selector_parent_class)->dispose) (object);
+}
 
 static void
 empathy_contact_selector_class_init (EmpathyContactSelectorClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  object_class->constructor = empathy_contact_selector_constructor;
-  object_class->dispose = empathy_contact_selector_dispose;
-  object_class->finalize = empathy_contact_selector_finalize;
-  object_class->set_property = empathy_contact_selector_set_property;
-  object_class->get_property = empathy_contact_selector_get_property;
+  object_class->constructor = contact_selector_constructor;
+  object_class->dispose = contact_selector_dispose;
+  object_class->set_property = contact_selector_set_property;
+  object_class->get_property = contact_selector_get_property;
   g_type_class_add_private (klass, sizeof (EmpathyContactSelectorPriv));
 
-  g_object_class_install_property (object_class, PROP_STORE,
-      g_param_spec_object ("store", "store", "store",
-      EMPATHY_TYPE_CONTACT_LIST_STORE, G_PARAM_CONSTRUCT_ONLY |
+  /**
+   * EmpathyContactSelector:contact-list:
+   *
+   * An #EmpathyContactList containing the contacts for the
+   * #EmpathyContactSelector.
+   */
+  g_object_class_install_property (object_class, PROP_CONTACT_LIST,
+      g_param_spec_object ("contact-list", "contact list", "contact list",
+      EMPATHY_TYPE_CONTACT_LIST, G_PARAM_CONSTRUCT_ONLY |
       G_PARAM_READWRITE | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
 }
 
+/**
+ * empathy_contact_selector_new:
+ * @contact_list: an #EmpathyContactList containing the contacts to list in
+ * the contact selector
+ *
+ * Creates a new #EmpathyContactSelector.
+ *
+ * Return value: A new #EmpathyContactSelector
+ */
+GtkWidget *
+empathy_contact_selector_new (EmpathyContactList *contact_list)
+{
+  g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (contact_list), NULL);
+
+  return GTK_WIDGET (g_object_new (EMPATHY_TYPE_CONTACT_SELECTOR,
+      "contact-list", contact_list, NULL));
+}
 
-EmpathyContactSelector *
-empathy_contact_selector_new (EmpathyContactListStore *store)
+/**
+ * empathy_contact_selector_dup_selected:
+ * @selector: An #EmpathyContactSelector
+ *
+ * Returns a new reference to the contact which is currently selected in
+ * @selector, or %NULL if there is no contact selected. The returned contact
+ * should be unrefed with g_object_unref() when finished with.
+ *
+ * Return value: A new reference to the contact currently selected, or %NULL
+ */
+EmpathyContact *
+empathy_contact_selector_dup_selected (EmpathyContactSelector *selector)
 {
-  return g_object_new (EMPATHY_TYPE_CONTACT_SELECTOR, "store", store, NULL);
+  EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
+  EmpathyContact *contact = NULL;
+  GtkTreeIter iter;
+
+  g_return_val_if_fail (EMPATHY_IS_CONTACT_SELECTOR (selector), NULL);
+
+  if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (selector), &iter))
+    return NULL;
+
+  gtk_tree_model_get (priv->model, &iter,
+      EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact, -1);
+
+  return contact;
 }
+
+typedef struct
+{
+  EmpathyContactSelectorFilterFunc func;
+  gpointer user_data;
+} FilterData;
+
+static void
+filter_data_free (gpointer data)
+{
+  g_slice_free (FilterData, data);
+}
+
+static gboolean
+contact_selector_filter_visible_func (GtkTreeModel *model,
+                                     GtkTreeIter *iter,
+                                     gpointer user_data)
+{
+  EmpathyContact *contact;
+  gboolean visible = TRUE;
+  FilterData *data = (FilterData *) user_data;
+
+  gtk_tree_model_get (model, iter,
+      EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
+      -1);
+
+  if (contact != NULL)
+    {
+      visible = data->func (contact, data->user_data);
+
+      g_object_unref (contact);
+    }
+
+  return visible;
+}
+
+/**
+ * empathy_contact_selector_set_visible:
+ * @selector: an #EmpathyContactSelector
+ * @func: an #EmpathyContactSelectorFilterFunc to filter the contacts
+ * @user_data: data to pass to @func or %NULL
+ *
+ * Sets a filter on the @selector so only contacts that return %TRUE
+ * when passed into @func are visible.
+ *
+ * A typical usage for this function would be to only show contacts that
+ * can send or receive files. In this case, one could use the
+ * empathy_contact_can_send_files() function
+ */
+void
+empathy_contact_selector_set_visible (EmpathyContactSelector *selector,
+                                      EmpathyContactSelectorFilterFunc func,
+                                      gpointer user_data)
+{
+  EmpathyContactSelectorPriv *priv = GET_PRIV (selector);
+  FilterData *data;
+
+  data = g_slice_new0 (FilterData);
+  data->func = func;
+  data->user_data = user_data;
+
+  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->model),
+      contact_selector_filter_visible_func, data, filter_data_free);
+
+  gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->model));
+}
+
+/**
+ * EmpathyContactSelectorFilterFunc:
+ * @contact: an #EmpathyContact
+ * @user_data: user data or %NULL
+ *
+ * A function which decides whether the contact indicated by @contact
+ * is visible.
+ *
+ * Return value: whether @contact is visible
+ */