[empathy] roster-view: add EmpathyRosterGroup and sort contacts accordingly
- From: Guillaume Desmottes <gdesmott src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [empathy] roster-view: add EmpathyRosterGroup and sort contacts accordingly
- Date: Thu, 14 Jun 2012 07:50:07 +0000 (UTC)
commit 2216d828732d19a32e99a485d829504bfff7d224
Author: Guillaume Desmottes <guillaume desmottes collabora co uk>
Date: Mon May 28 15:28:59 2012 +0200
roster-view: add EmpathyRosterGroup and sort contacts accordingly
The GtkExpander doesn't contain the contacts as its children because the view
needs to be have full control of which rows as displayed (for live search for
example). So instead we trick the view sort function to display the contact
associated with the group at the right position.
Also, we need to keep the structure flat to not break keyboard navigation
inside the widget.
libempathy-gtk/empathy-roster-view.c | 256 ++++++++++++++++++++++++++++++++--
1 files changed, 244 insertions(+), 12 deletions(-)
---
diff --git a/libempathy-gtk/empathy-roster-view.c b/libempathy-gtk/empathy-roster-view.c
index 0212ee1..6281cf2 100644
--- a/libempathy-gtk/empathy-roster-view.c
+++ b/libempathy-gtk/empathy-roster-view.c
@@ -131,6 +131,51 @@ add_roster_contact (EmpathyRosterView *self,
}
static void
+group_expanded_cb (EmpathyRosterGroup *group,
+ GParamSpec *spec,
+ EmpathyRosterView *self)
+{
+ GList *widgets, *l;
+
+ widgets = empathy_roster_group_get_widgets (group);
+ for (l = widgets; l != NULL; l = g_list_next (l))
+ {
+ egg_list_box_child_changed (EGG_LIST_BOX (self), l->data);
+ }
+
+ g_list_free (widgets);
+}
+
+static EmpathyRosterGroup *
+lookup_roster_group (EmpathyRosterView *self,
+ const gchar *group)
+{
+ return g_hash_table_lookup (self->priv->roster_groups, group);
+}
+
+static void
+ensure_roster_group (EmpathyRosterView *self,
+ const gchar *group)
+{
+ GtkWidget *roster_group;
+
+ roster_group = (GtkWidget *) lookup_roster_group (self, group);
+ if (roster_group != NULL)
+ return;
+
+ roster_group = empathy_roster_group_new (group);
+
+ g_signal_connect (roster_group, "notify::expanded",
+ G_CALLBACK (group_expanded_cb), self);
+
+ gtk_widget_show (roster_group);
+ gtk_container_add (GTK_CONTAINER (self), roster_group);
+
+ g_hash_table_insert (self->priv->roster_groups, g_strdup (group),
+ roster_group);
+}
+
+static void
add_to_group (EmpathyRosterView *self,
FolksIndividual *individual,
const gchar *group)
@@ -142,7 +187,9 @@ add_to_group (EmpathyRosterView *self,
if (contacts == NULL)
return;
- /* TODO: ensure group widget */
+ if (tp_strdiff (group, NO_GROUP))
+ ensure_roster_group (self, group);
+
contact = add_roster_contact (self, individual, group);
g_hash_table_insert (contacts, g_strdup (group), contact);
}
@@ -196,21 +243,54 @@ individual_added (EmpathyRosterView *self,
}
static void
+update_group_widgets_count (EmpathyRosterView *self,
+ EmpathyRosterGroup *group,
+ EmpathyRosterContact *contact,
+ gboolean displayed)
+{
+ if (displayed)
+ {
+ if (empathy_roster_group_add_widget (group, GTK_WIDGET (contact)) == 1)
+ {
+ egg_list_box_child_changed (EGG_LIST_BOX (self),
+ GTK_WIDGET (group));
+ }
+ }
+ else
+ {
+ if (empathy_roster_group_remove_widget (group, GTK_WIDGET (contact)) == 0)
+ {
+ egg_list_box_child_changed (EGG_LIST_BOX (self),
+ GTK_WIDGET (group));
+ }
+ }
+}
+
+static void
individual_removed (EmpathyRosterView *self,
FolksIndividual *individual)
{
GHashTable *contacts;
GHashTableIter iter;
- gpointer value;
+ gpointer key, value;
contacts = g_hash_table_lookup (self->priv->roster_contacts, individual);
if (contacts == NULL)
return;
g_hash_table_iter_init (&iter, contacts);
- while (g_hash_table_iter_next (&iter, NULL, &value))
+ while (g_hash_table_iter_next (&iter, &key, &value))
{
+ const gchar *group_name = key;
GtkWidget *contact = value;
+ EmpathyRosterGroup *group;
+
+ group = lookup_roster_group (self, group_name);
+ if (group != NULL)
+ {
+ update_group_widgets_count (self, group,
+ EMPATHY_ROSTER_CONTACT (contact), FALSE);
+ }
gtk_container_remove (GTK_CONTAINER (self), contact);
}
@@ -244,9 +324,8 @@ members_changed_cb (EmpathyIndividualManager *manager,
}
static gint
-roster_view_sort (EmpathyRosterContact *a,
- EmpathyRosterContact *b,
- EmpathyRosterView *self)
+compare_roster_contacts_by_alias (EmpathyRosterContact *a,
+ EmpathyRosterContact *b)
{
FolksIndividual *ind_a, *ind_b;
const gchar *alias_a, *alias_b;
@@ -260,6 +339,102 @@ roster_view_sort (EmpathyRosterContact *a,
return g_ascii_strcasecmp (alias_a, alias_b);
}
+static gint
+compare_roster_contacts_no_group (EmpathyRosterView *self,
+ EmpathyRosterContact *a,
+ EmpathyRosterContact *b)
+{
+ return compare_roster_contacts_by_alias (a, b);
+}
+
+static gint
+compare_group_names (const gchar *group_a,
+ const gchar *group_b)
+{
+ return g_ascii_strcasecmp (group_a, group_b);
+}
+
+static gint
+compare_roster_contacts_with_groups (EmpathyRosterView *self,
+ EmpathyRosterContact *a,
+ EmpathyRosterContact *b)
+{
+ const gchar *group_a, *group_b;
+
+ group_a = empathy_roster_contact_get_group (a);
+ group_b = empathy_roster_contact_get_group (b);
+
+ if (!tp_strdiff (group_a, group_b))
+ /* Same group, compare the contacts */
+ return compare_roster_contacts_by_alias (a, b);
+
+ /* Sort by group */
+ return compare_group_names (group_a, group_b);
+}
+
+static gint
+compare_roster_contacts (EmpathyRosterView *self,
+ EmpathyRosterContact *a,
+ EmpathyRosterContact *b)
+{
+ if (!self->priv->show_groups)
+ return compare_roster_contacts_no_group (self, a, b);
+ else
+ return compare_roster_contacts_with_groups (self, a, b);
+}
+
+static gint
+compare_roster_groups (EmpathyRosterGroup *a,
+ EmpathyRosterGroup *b)
+{
+ const gchar *name_a, *name_b;
+
+ name_a = empathy_roster_group_get_name (a);
+ name_b = empathy_roster_group_get_name (b);
+
+ return compare_group_names (name_a, name_b);
+}
+
+static gint
+compare_contact_group (EmpathyRosterContact *contact,
+ EmpathyRosterGroup *group)
+{
+ const char *contact_group, *group_name;
+
+ contact_group = empathy_roster_contact_get_group (contact);
+ group_name = empathy_roster_group_get_name (group);
+
+ if (!tp_strdiff (contact_group, group_name))
+ /* @contact is in @group, @group has to be displayed first */
+ return 1;
+
+ /* @contact is in a different group, sort by group name */
+ return compare_group_names (contact_group, group_name);
+}
+
+static gint
+roster_view_sort (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ EmpathyRosterView *self = user_data;
+
+ if (EMPATHY_IS_ROSTER_CONTACT (a) && EMPATHY_IS_ROSTER_CONTACT (b))
+ return compare_roster_contacts (self, EMPATHY_ROSTER_CONTACT (a),
+ EMPATHY_ROSTER_CONTACT (b));
+ else if (EMPATHY_IS_ROSTER_GROUP (a) && EMPATHY_IS_ROSTER_GROUP (b))
+ return compare_roster_groups (EMPATHY_ROSTER_GROUP (a),
+ EMPATHY_ROSTER_GROUP (b));
+ else if (EMPATHY_IS_ROSTER_CONTACT (a) && EMPATHY_IS_ROSTER_GROUP (b))
+ return compare_contact_group (EMPATHY_ROSTER_CONTACT (a),
+ EMPATHY_ROSTER_GROUP (b));
+ else if (EMPATHY_IS_ROSTER_GROUP (a) && EMPATHY_IS_ROSTER_CONTACT (b))
+ return -1 * compare_contact_group (EMPATHY_ROSTER_CONTACT (b),
+ EMPATHY_ROSTER_GROUP (a));
+
+ g_return_val_if_reached (0);
+}
+
static void
update_separator (GtkWidget **separator,
GtkWidget *child,
@@ -281,16 +456,60 @@ update_separator (GtkWidget **separator,
}
static gboolean
+filter_contact (EmpathyRosterView *self,
+ EmpathyRosterContact *contact)
+{
+ gboolean displayed;
+
+ if (self->priv->show_offline)
+ {
+ displayed = TRUE;
+ }
+ else
+ {
+ displayed = empathy_roster_contact_is_online (contact);
+ }
+
+ if (self->priv->show_groups)
+ {
+ const gchar *group_name;
+ EmpathyRosterGroup *group;
+
+ group_name = empathy_roster_contact_get_group (contact);
+ group = lookup_roster_group (self, group_name);
+
+ if (group != NULL)
+ {
+ update_group_widgets_count (self, group, contact, displayed);
+
+ if (!gtk_expander_get_expanded (GTK_EXPANDER (group)))
+ return FALSE;
+ }
+ }
+
+ return displayed;
+}
+
+static gboolean
+filter_group (EmpathyRosterView *self,
+ EmpathyRosterGroup *group)
+{
+ return empathy_roster_group_get_widgets_count (group);
+}
+
+static gboolean
filter_list (GtkWidget *child,
gpointer user_data)
{
EmpathyRosterView *self = user_data;
- EmpathyRosterContact *contact = EMPATHY_ROSTER_CONTACT (child);
- if (self->priv->show_offline)
- return TRUE;
+ if (EMPATHY_IS_ROSTER_CONTACT (child))
+ return filter_contact (self, EMPATHY_ROSTER_CONTACT (child));
+
+ else if (EMPATHY_IS_ROSTER_GROUP (child))
+ return filter_group (self, EMPATHY_ROSTER_GROUP (child));
- return empathy_roster_contact_is_online (contact);
+ g_return_val_if_reached (FALSE);
}
static void
@@ -316,6 +535,7 @@ remove_from_group (EmpathyRosterView *self,
{
GHashTable *contacts;
GtkWidget *contact;
+ EmpathyRosterGroup *roster_group;
contacts = g_hash_table_lookup (self->priv->roster_contacts, individual);
if (contacts == NULL)
@@ -325,13 +545,22 @@ remove_from_group (EmpathyRosterView *self,
if (contact == NULL)
return;
- gtk_container_remove (GTK_CONTAINER (self), contact);
g_hash_table_remove (contacts, group);
if (g_hash_table_size (contacts) == 0)
{
add_to_group (self, individual, UNGROUPPED);
}
+
+ roster_group = lookup_roster_group (self, group);
+
+ if (roster_group != NULL)
+ {
+ update_group_widgets_count (self, roster_group,
+ EMPATHY_ROSTER_CONTACT (contact), FALSE);
+ }
+
+ gtk_container_remove (GTK_CONTAINER (self), contact);
}
static void
@@ -374,7 +603,7 @@ empathy_roster_view_constructed (GObject *object)
G_CALLBACK (groups_changed_cb), self, 0);
egg_list_box_set_sort_func (EGG_LIST_BOX (self),
- (GCompareDataFunc) roster_view_sort, self, NULL);
+ roster_view_sort, self, NULL);
egg_list_box_set_separator_funcs (EGG_LIST_BOX (self), update_separator,
self, NULL);
@@ -403,6 +632,7 @@ empathy_roster_view_finalize (GObject *object)
((GObjectClass *) empathy_roster_view_parent_class)->finalize;
g_hash_table_unref (self->priv->roster_contacts);
+ g_hash_table_unref (self->priv->roster_groups);
if (chain_up != NULL)
chain_up (object);
@@ -450,6 +680,8 @@ empathy_roster_view_init (EmpathyRosterView *self)
self->priv->roster_contacts = g_hash_table_new_full (NULL, NULL,
NULL, (GDestroyNotify) g_hash_table_unref);
+ self->priv->roster_groups = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, NULL);
}
GtkWidget *
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]