aboutsummaryrefslogtreecommitdiffstats
path: root/libempathy-gtk
diff options
context:
space:
mode:
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2012-05-28 21:28:59 +0800
committerGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2012-06-14 15:21:49 +0800
commit2216d828732d19a32e99a485d829504bfff7d224 (patch)
tree68fa1cd99a498123ef670dcca20be594e8a5d12a /libempathy-gtk
parent043d63caaf16e09c291f0dcd28068e0efbf97bdb (diff)
downloadgsoc2013-empathy-2216d828732d19a32e99a485d829504bfff7d224.tar
gsoc2013-empathy-2216d828732d19a32e99a485d829504bfff7d224.tar.gz
gsoc2013-empathy-2216d828732d19a32e99a485d829504bfff7d224.tar.bz2
gsoc2013-empathy-2216d828732d19a32e99a485d829504bfff7d224.tar.lz
gsoc2013-empathy-2216d828732d19a32e99a485d829504bfff7d224.tar.xz
gsoc2013-empathy-2216d828732d19a32e99a485d829504bfff7d224.tar.zst
gsoc2013-empathy-2216d828732d19a32e99a485d829504bfff7d224.zip
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.
Diffstat (limited to 'libempathy-gtk')
-rw-r--r--libempathy-gtk/empathy-roster-view.c256
1 files changed, 244 insertions, 12 deletions
diff --git a/libempathy-gtk/empathy-roster-view.c b/libempathy-gtk/empathy-roster-view.c
index 0212ee1aa..6281cf2e6 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 *