diff options
author | Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> | 2012-05-28 21:28:59 +0800 |
---|---|---|
committer | Guillaume Desmottes <guillaume.desmottes@collabora.co.uk> | 2012-06-14 15:21:49 +0800 |
commit | 2216d828732d19a32e99a485d829504bfff7d224 (patch) | |
tree | 68fa1cd99a498123ef670dcca20be594e8a5d12a | |
parent | 043d63caaf16e09c291f0dcd28068e0efbf97bdb (diff) | |
download | gsoc2013-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.
-rw-r--r-- | 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 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 * |