From: Guillaume Desmottes Date: Fri, 10 Aug 2012 15:23:52 +0000 (+0200) Subject: Merge remote-tracking branch 'burton/aggregator' X-Git-Url: https://git.0d.be/?p=empathy.git;a=commitdiff_plain;h=a5346f80e2d9b5f2fac7909e3688a6ae69d823a8;hp=53257c1959bb7575cf72868e5ce798f5984d62d8 Merge remote-tracking branch 'burton/aggregator' --- diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am index 87d78752..902a5e57 100644 --- a/libempathy-gtk/Makefile.am +++ b/libempathy-gtk/Makefile.am @@ -77,6 +77,7 @@ libempathy_gtk_handwritten_source = \ empathy-roster-contact.c \ empathy-roster-group.c \ empathy-roster-model.c \ + empathy-roster-model-aggregator.c \ empathy-roster-model-manager.c \ empathy-roster-view.c \ empathy-search-bar.c \ @@ -148,6 +149,7 @@ libempathy_gtk_headers = \ empathy-roster-contact.h \ empathy-roster-group.h \ empathy-roster-model.h \ + empathy-roster-model-aggregator.h \ empathy-roster-model-manager.h \ empathy-roster-view.h \ empathy-search-bar.h \ diff --git a/libempathy-gtk/empathy-roster-model-aggregator.c b/libempathy-gtk/empathy-roster-model-aggregator.c new file mode 100644 index 00000000..b0b68449 --- /dev/null +++ b/libempathy-gtk/empathy-roster-model-aggregator.c @@ -0,0 +1,425 @@ +/* + * empathy-roster-model-aggregator.c + * + * Implementation of EmpathyRosterModel using FolksIndividualAggregator as + * source. + * + * Copyright (C) 2012 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "config.h" + +#include +#include + +#include "empathy-roster-model-aggregator.h" + +/** + * SECTION: empathy-roster-model-aggregator + * @title: EmpathyRosterModelAggregator + * @short_description: TODO + * + * TODO + */ + +/** + * EmpathyRosterModelAggregator: + * + * Data structure representing a #EmpathyRosterModelAggregator. + * + * Since: UNRELEASED + */ + +/** + * EmpathyRosterModelAggregatorClass: + * + * The class of a #EmpathyRosterModelAggregator. + * + * Since: UNRELEASED + */ + +static void roster_model_iface_init (EmpathyRosterModelInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (EmpathyRosterModelAggregator, + empathy_roster_model_aggregator, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_ROSTER_MODEL, roster_model_iface_init)) + +enum +{ + PROP_AGGREGATOR = 1, + PROP_FILTER_FUNC, + PROP_FILTER_DATA, + N_PROPS +}; + +/* +enum +{ + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; +*/ + +struct _EmpathyRosterModelAggregatorPriv +{ + FolksIndividualAggregator *aggregator; + GHashTable *filtered_individuals; /* Individual -> Individual */ + + EmpathyRosterModelAggregatorFilterFunc filter_func; + gpointer filter_data; +}; + +static void +individual_group_changed_cb (FolksIndividual *individual, + gchar *group, + gboolean is_member, + EmpathyRosterModelAggregator *self) +{ + empathy_roster_model_fire_groups_changed (EMPATHY_ROSTER_MODEL (self), + individual, group, is_member); +} + +static void +add_to_filtered_individuals (EmpathyRosterModelAggregator *self, + FolksIndividual *individual) +{ + g_hash_table_add (self->priv->filtered_individuals, + g_object_ref (individual)); + + tp_g_signal_connect_object (individual, "group-changed", + G_CALLBACK (individual_group_changed_cb), self, 0); + + empathy_roster_model_fire_individual_added (EMPATHY_ROSTER_MODEL (self), + individual); +} + +static void +remove_from_filtered_individuals (EmpathyRosterModelAggregator *self, + FolksIndividual *individual) +{ + g_signal_handlers_disconnect_by_func (individual, + individual_group_changed_cb, self); + + g_hash_table_remove (self->priv->filtered_individuals, individual); + + empathy_roster_model_fire_individual_removed (EMPATHY_ROSTER_MODEL (self), + individual); +} + +static void +individual_notify_cb (FolksIndividual *individual, + GParamSpec *param, + EmpathyRosterModelAggregator *self) +{ + if (!self->priv->filter_func (EMPATHY_ROSTER_MODEL (self), individual, self) + && g_hash_table_contains (self->priv->filtered_individuals, individual)) + remove_from_filtered_individuals (self, individual); + + if (self->priv->filter_func (EMPATHY_ROSTER_MODEL (self), individual, self) + && !g_hash_table_contains (self->priv->filtered_individuals, individual)) + add_to_filtered_individuals (self, individual); +} + +static void +add_individual (EmpathyRosterModelAggregator *self, + FolksIndividual *individual) +{ + if (self->priv->filter_func != NULL) + { + tp_g_signal_connect_object (individual, "notify", + G_CALLBACK (individual_notify_cb), self, 0); + + if (!self->priv->filter_func (EMPATHY_ROSTER_MODEL (self), individual, + self)) + return; + } + + add_to_filtered_individuals (self, individual); +} + +static void +remove_individual (EmpathyRosterModelAggregator *self, + FolksIndividual *individual) +{ + if (self->priv->filter_func != NULL) + g_signal_handlers_disconnect_by_func (individual, + individual_notify_cb, self); + + if (g_hash_table_contains (self->priv->filtered_individuals, + individual)) + remove_from_filtered_individuals (self, individual); +} + +static void +populate_individuals (EmpathyRosterModelAggregator *self) +{ + GeeMap *individuals; + GeeMapIterator *iter; + + individuals = folks_individual_aggregator_get_individuals ( + self->priv->aggregator); + iter = gee_map_map_iterator (individuals); + while (gee_map_iterator_next (iter)) + { + add_individual (self, gee_map_iterator_get_value (iter)); + } + g_clear_object (&iter); +} + +static void +aggregator_individuals_changed_cb (FolksIndividualAggregator *aggregator, + GeeSet *added, + GeeSet *removed, + gchar *message, + FolksPersona *actor, + FolksGroupDetailsChangeReason reason, + EmpathyRosterModelAggregator *self) +{ + if (gee_collection_get_size (GEE_COLLECTION (added)) > 0) + { + GeeIterator *iter = gee_iterable_iterator (GEE_ITERABLE (added)); + + while (iter != NULL && gee_iterator_next (iter)) + { + add_individual (self, gee_iterator_get (iter)); + } + g_clear_object (&iter); + } + + if (gee_collection_get_size (GEE_COLLECTION (removed)) > 0) + { + GeeIterator *iter = gee_iterable_iterator (GEE_ITERABLE (removed)); + + while (iter != NULL && gee_iterator_next (iter)) + { + remove_individual (self, gee_iterator_get (iter)); + } + g_clear_object (&iter); + } +} + +static void +empathy_roster_model_aggregator_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (object); + + switch (property_id) + { + case PROP_AGGREGATOR: + g_value_set_object (value, self->priv->aggregator); + break; + case PROP_FILTER_FUNC: + g_value_set_pointer (value, self->priv->filter_func); + break; + case PROP_FILTER_DATA: + g_value_set_pointer (value, self->priv->filter_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +empathy_roster_model_aggregator_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (object); + + switch (property_id) + { + case PROP_AGGREGATOR: + g_assert (self->priv->aggregator == NULL); /* construct only */ + self->priv->aggregator = g_value_dup_object (value); + break; + case PROP_FILTER_FUNC: + g_assert (self->priv->filter_func == NULL); /* construct only */ + self->priv->filter_func = g_value_get_pointer (value); + break; + case PROP_FILTER_DATA: + g_assert (self->priv->filter_data == NULL); /* construct only */ + self->priv->filter_data = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +empathy_roster_model_aggregator_constructed (GObject *object) +{ + EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (object); + void (*chain_up) (GObject *) = + ((GObjectClass *) empathy_roster_model_aggregator_parent_class)->constructed; + + if (chain_up != NULL) + chain_up (object); + + if (self->priv->aggregator == NULL) + self->priv->aggregator = folks_individual_aggregator_new (); + + g_assert (FOLKS_IS_INDIVIDUAL_AGGREGATOR (self->priv->aggregator)); + + tp_g_signal_connect_object (self->priv->aggregator, "individuals-changed", + G_CALLBACK (aggregator_individuals_changed_cb), self, 0); + + folks_individual_aggregator_prepare (self->priv->aggregator, NULL, NULL); + + populate_individuals (self); +} + +static void +empathy_roster_model_aggregator_dispose (GObject *object) +{ + EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (object); + void (*chain_up) (GObject *) = + ((GObjectClass *) empathy_roster_model_aggregator_parent_class)->dispose; + + g_clear_object (&self->priv->aggregator); + g_clear_pointer (&self->priv->filtered_individuals, g_hash_table_unref); + + if (chain_up != NULL) + chain_up (object); +} + +static void +empathy_roster_model_aggregator_finalize (GObject *object) +{ + //EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (object); + void (*chain_up) (GObject *) = + ((GObjectClass *) empathy_roster_model_aggregator_parent_class)->finalize; + + if (chain_up != NULL) + chain_up (object); +} + +static void +empathy_roster_model_aggregator_class_init ( + EmpathyRosterModelAggregatorClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + GParamSpec *spec; + + oclass->get_property = empathy_roster_model_aggregator_get_property; + oclass->set_property = empathy_roster_model_aggregator_set_property; + oclass->constructed = empathy_roster_model_aggregator_constructed; + oclass->dispose = empathy_roster_model_aggregator_dispose; + oclass->finalize = empathy_roster_model_aggregator_finalize; + + spec = g_param_spec_object ("aggregator", "Aggregator", + "FolksIndividualAggregator", + FOLKS_TYPE_INDIVIDUAL_AGGREGATOR, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_AGGREGATOR, spec); + + spec = g_param_spec_pointer ("filter-func", "Filter-Func", + "EmpathyRosterModelAggregatorFilterFunc", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_FILTER_FUNC, spec); + + spec = g_param_spec_pointer ("filter-data", "Filter-Data", + "GPointer", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + g_object_class_install_property (oclass, PROP_FILTER_DATA, spec); + + g_type_class_add_private (klass, sizeof (EmpathyRosterModelAggregatorPriv)); +} + +static void +empathy_roster_model_aggregator_init (EmpathyRosterModelAggregator *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR, EmpathyRosterModelAggregatorPriv); + + self->priv->filtered_individuals = g_hash_table_new_full (NULL, NULL, NULL, + g_object_unref); +} + +EmpathyRosterModelAggregator * +empathy_roster_model_aggregator_new ( + EmpathyRosterModelAggregatorFilterFunc filter_func, + gpointer user_data) +{ + return g_object_new (EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR, + "filter-func", filter_func, + "filter-data", user_data, + NULL); +} + +EmpathyRosterModelAggregator * +empathy_roster_model_aggregator_new_with_aggregator ( + FolksIndividualAggregator *aggregator, + EmpathyRosterModelAggregatorFilterFunc filter_func, + gpointer user_data) +{ + g_return_val_if_fail (FOLKS_IS_INDIVIDUAL_AGGREGATOR (aggregator), NULL); + + return g_object_new (EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR, + "aggregator", aggregator, + "filter-func", filter_func, + "filter-data", user_data, + NULL); +} + +static GList * +empathy_roster_model_aggregator_get_individuals (EmpathyRosterModel *model) +{ + EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (model); + + return g_hash_table_get_values (self->priv->filtered_individuals); +} + +static GList * +empathy_roster_model_aggregator_get_groups_for_individual ( + EmpathyRosterModel *model, + FolksIndividual *individual) +{ + GList *groups_list = NULL; + GeeSet *groups_set; + + groups_set = folks_group_details_get_groups ( + FOLKS_GROUP_DETAILS (individual)); + if (gee_collection_get_size (GEE_COLLECTION (groups_set)) > 0) + { + GeeIterator *iter = gee_iterable_iterator (GEE_ITERABLE (groups_set)); + + while (iter != NULL && gee_iterator_next (iter)) + { + groups_list = g_list_prepend (groups_list, gee_iterator_get (iter)); + } + g_clear_object (&iter); + } + + return groups_list; +} + +static void +roster_model_iface_init (EmpathyRosterModelInterface *iface) +{ + iface->get_individuals = empathy_roster_model_aggregator_get_individuals; + iface->get_groups_for_individual = + empathy_roster_model_aggregator_get_groups_for_individual; +} diff --git a/libempathy-gtk/empathy-roster-model-aggregator.h b/libempathy-gtk/empathy-roster-model-aggregator.h new file mode 100644 index 00000000..e29e36eb --- /dev/null +++ b/libempathy-gtk/empathy-roster-model-aggregator.h @@ -0,0 +1,93 @@ +/* + * empathy-roster-model-aggregator.h + * + * Copyright (C) 2012 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef __EMPATHY_ROSTER_MODEL_AGGREGATOR_H__ +#define __EMPATHY_ROSTER_MODEL_AGGREGATOR_H__ + +#include + +#include + +#include "empathy-roster-model.h" + +G_BEGIN_DECLS + +typedef struct _EmpathyRosterModelAggregator EmpathyRosterModelAggregator; +typedef struct _EmpathyRosterModelAggregatorClass +EmpathyRosterModelAggregatorClass; +typedef struct _EmpathyRosterModelAggregatorPriv +EmpathyRosterModelAggregatorPriv; + +struct _EmpathyRosterModelAggregatorClass +{ + /**/ + GObjectClass parent_class; +}; + +struct _EmpathyRosterModelAggregator +{ + /**/ + GObject parent; + EmpathyRosterModelAggregatorPriv *priv; +}; + +typedef gboolean (* EmpathyRosterModelAggregatorFilterFunc) ( + EmpathyRosterModel *model, + FolksIndividual *individual, + gpointer user_data); + +GType empathy_roster_model_aggregator_get_type (void); + +/* TYPE MACROS */ +#define EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR \ + (empathy_roster_model_aggregator_get_type ()) +#define EMPATHY_ROSTER_MODEL_AGGREGATOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR, \ + EmpathyRosterModelAggregator)) +#define EMPATHY_ROSTER_MODEL_AGGREGATOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), \ + EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR, \ + EmpathyRosterModelAggregatorClass)) +#define EMPATHY_IS_ROSTER_MODEL_AGGREGATOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR)) +#define EMPATHY_IS_ROSTER_MODEL_AGGREGATOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), \ + EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR)) +#define EMPATHY_ROSTER_MODEL_AGGREGATOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR, \ + EmpathyRosterModelAggregatorClass)) + +EmpathyRosterModelAggregator * empathy_roster_model_aggregator_new ( + EmpathyRosterModelAggregatorFilterFunc filter_func, + gpointer user_data); + +EmpathyRosterModelAggregator * +empathy_roster_model_aggregator_new_with_aggregator ( + FolksIndividualAggregator *aggregator, + EmpathyRosterModelAggregatorFilterFunc filter_func, + gpointer user_data); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_ROSTER_MODEL_AGGREGATOR_H__*/ diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am index d741f24d..27fdbf7e 100644 --- a/tests/interactive/Makefile.am +++ b/tests/interactive/Makefile.am @@ -20,7 +20,8 @@ noinst_PROGRAMS = \ test-empathy-account-chooser \ test-empathy-calendar-button \ test-empathy-roster-view \ - test-empathy-dual-roster-view + test-empathy-dual-roster-view \ + test-empathy-roster-model-aggregator empathy_logs_SOURCES = empathy-logs.c test_empathy_contact_blocking_dialog_SOURCES = test-empathy-contact-blocking-dialog.c @@ -31,3 +32,4 @@ test_empathy_account_chooser_SOURCES = test-empathy-account-chooser.c test_empathy_calendar_button_SOURCES = test-empathy-calendar-button.c test_empathy_roster_view_SOURCES = test-empathy-roster-view.c test_empathy_dual_roster_view_SOURCES = test-empathy-dual-roster-view.c +test_empathy_roster_model_aggregator_SOURCES = test-empathy-roster-model-aggregator.c diff --git a/tests/interactive/test-empathy-roster-model-aggregator.c b/tests/interactive/test-empathy-roster-model-aggregator.c new file mode 100644 index 00000000..49f2012f --- /dev/null +++ b/tests/interactive/test-empathy-roster-model-aggregator.c @@ -0,0 +1,164 @@ +#include + +#include +#include + +#include +#include + +static gboolean show_offline = FALSE; +static gboolean show_groups = FALSE; + +static GOptionEntry entries[] = + { + { "offline", 0, 0, G_OPTION_ARG_NONE, &show_offline, "Show offline contacts", NULL }, + { "groups", 0, 0, G_OPTION_ARG_NONE, &show_groups, "Show groups", NULL }, + { NULL } + }; + +static void +individual_activated_cb (EmpathyRosterView *self, + FolksIndividual *individual, + gpointer user_data) +{ + g_assert (FOLKS_IS_INDIVIDUAL (individual)); + + g_print ("'%s' activated\n", + folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual))); +} + +static void +popup_individual_menu_cb (EmpathyRosterView *self, + FolksIndividual *individual, + guint button, + guint time, + gpointer user_data) +{ + GtkWidget *menu, *item; + + g_print ("'%s' popup menu\n", + folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual))); + + menu = gtk_menu_new (); + + g_signal_connect (menu, "deactivate", + G_CALLBACK (gtk_widget_destroy), NULL); + + item = gtk_menu_item_new_with_label (folks_alias_details_get_alias ( + FOLKS_ALIAS_DETAILS (individual))); + gtk_widget_show (item); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (self), NULL); + + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, time); +} + +static gboolean +individual_tooltip_cb (EmpathyRosterView *view, + FolksIndividual *individual, + gboolean keyboard_mode, + GtkTooltip *tooltip, + gpointer user_data) +{ + gtk_tooltip_set_text (tooltip, + folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual))); + + return TRUE; +} + +static void +empty_cb (EmpathyRosterView *view, + GParamSpec *spec, + gpointer user_data) +{ + if (empathy_roster_view_is_empty (view)) + g_print ("view is now empty\n"); + else + g_print ("view is no longer empty\n"); +} + +static gboolean +filter (EmpathyRosterModel *model, + FolksIndividual *individual, + gpointer user_data) +{ + if (folks_avatar_details_get_avatar (FOLKS_AVATAR_DETAILS (individual)) + == NULL) + return FALSE; + + return TRUE; +} + +int +main (int argc, + char **argv) +{ + GtkWidget *window, *view, *scrolled, *box, *search; + GError *error = NULL; + GOptionContext *context; + EmpathyRosterModel *model; + + gtk_init (&argc, &argv); + empathy_gtk_init (); + + context = g_option_context_new ("- test tree model performance"); + g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_print ("option parsing failed: %s\n", error->message); + return 1; + } + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + empathy_set_css_provider (window); + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8); + + model = EMPATHY_ROSTER_MODEL (empathy_roster_model_aggregator_new ( + filter, NULL)); + view = empathy_roster_view_new (model); + + g_object_unref (model); + g_signal_connect (view, "individual-activated", + G_CALLBACK (individual_activated_cb), NULL); + g_signal_connect (view, "popup-individual-menu", + G_CALLBACK (popup_individual_menu_cb), NULL); + g_signal_connect (view, "notify::empty", + G_CALLBACK (empty_cb), NULL); + g_signal_connect (view, "individual-tooltip", + G_CALLBACK (individual_tooltip_cb), NULL); + + gtk_widget_set_has_tooltip (view, TRUE); + + empathy_roster_view_show_offline (EMPATHY_ROSTER_VIEW (view), show_offline); + empathy_roster_view_show_groups (EMPATHY_ROSTER_VIEW (view), show_groups); + + search = empathy_live_search_new (view); + empathy_roster_view_set_live_search (EMPATHY_ROSTER_VIEW (view), + EMPATHY_LIVE_SEARCH (search)); + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + + egg_list_box_add_to_scrolled (EGG_LIST_BOX (view), + GTK_SCROLLED_WINDOW (scrolled)); + + gtk_box_pack_start (GTK_BOX (box), search, FALSE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (box), scrolled, TRUE, TRUE, 0); + gtk_container_add (GTK_CONTAINER (window), box); + + gtk_window_set_default_size (GTK_WINDOW (window), 300, 600); + gtk_widget_show_all (window); + + g_signal_connect_swapped (window, "destroy", + G_CALLBACK (gtk_main_quit), NULL); + + gtk_main (); + + return 0; +}