1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2005-2007 Imendio AB
4 * Copyright (C) 2007-2008 Collabora Ltd.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Martyn Russell <martyn@imendio.com>
23 * Xavier Claessens <xclaesse@gmail.com>
33 #include <telepathy-glib/util.h>
35 #include <libempathy/empathy-utils.h>
36 #include <libempathy/empathy-contact-list.h>
37 #include "empathy-contact-list-store.h"
38 #include "empathy-ui-utils.h"
39 #include "empathy-gtk-enum-types.h"
41 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
42 #include <libempathy/empathy-debug.h>
44 /* Active users are those which have recently changed state
45 * (e.g. online, offline or from normal to a busy state).
48 /* Time in seconds user is shown as active */
49 #define ACTIVE_USER_SHOW_TIME 7
51 /* Time in seconds after connecting which we wait before active users are enabled */
52 #define ACTIVE_USER_WAIT_TO_ENABLE_TIME 5
54 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContactListStore)
56 EmpathyContactList *list;
57 gboolean show_offline;
58 gboolean show_avatars;
62 EmpathyContactListStoreSort sort_criterium;
64 } EmpathyContactListStorePriv;
73 EmpathyContact *contact;
79 EmpathyContactListStore *store;
80 EmpathyContact *contact;
84 static void contact_list_store_finalize (GObject *object);
85 static void contact_list_store_get_property (GObject *object,
89 static void contact_list_store_set_property (GObject *object,
93 static void contact_list_store_setup (EmpathyContactListStore *store);
94 static gboolean contact_list_store_inibit_active_cb (EmpathyContactListStore *store);
95 static void contact_monitor_contact_added_cb (EmpathyContactMonitor *monitor,
96 EmpathyContact *contact,
97 EmpathyContactListStore *store);
98 static void contact_list_store_groups_changed_cb (EmpathyContactList *list_iface,
99 EmpathyContact *contact,
102 EmpathyContactListStore *store);
103 static void contact_list_store_add_contact (EmpathyContactListStore *store,
104 EmpathyContact *contact);
105 static void contact_list_store_remove_contact (EmpathyContactListStore *store,
106 EmpathyContact *contact);
107 static void contact_list_store_contact_update (EmpathyContactListStore *store,
108 EmpathyContact *contact);
109 static void contact_list_store_contact_set_active (EmpathyContactListStore *store,
110 EmpathyContact *contact,
112 gboolean set_changed);
113 static ShowActiveData * contact_list_store_contact_active_new (EmpathyContactListStore *store,
114 EmpathyContact *contact,
116 static void contact_list_store_contact_active_free (ShowActiveData *data);
117 static gboolean contact_list_store_contact_active_cb (ShowActiveData *data);
118 static gboolean contact_list_store_get_group_foreach (GtkTreeModel *model,
122 static void contact_list_store_get_group (EmpathyContactListStore *store,
124 GtkTreeIter *iter_group_to_set,
125 GtkTreeIter *iter_separator_to_set,
127 static gint contact_list_store_state_sort_func (GtkTreeModel *model,
131 static gint contact_list_store_name_sort_func (GtkTreeModel *model,
135 static gboolean contact_list_store_find_contact_foreach (GtkTreeModel *model,
139 static GList * contact_list_store_find_contact (EmpathyContactListStore *store,
140 EmpathyContact *contact);
141 static gboolean contact_list_store_update_list_mode_foreach (GtkTreeModel *model,
144 EmpathyContactListStore *store);
145 static gboolean contact_list_store_iface_setup (gpointer user_data);
157 G_DEFINE_TYPE (EmpathyContactListStore, empathy_contact_list_store, GTK_TYPE_TREE_STORE);
161 contact_list_store_set_contact_list (EmpathyContactListStore *store,
162 EmpathyContactList *list_iface)
164 EmpathyContactListStorePriv *priv = GET_PRIV (store);
166 priv->list = g_object_ref (list_iface);
168 /* Let a chance to have all properties set before populating */
169 g_idle_add (contact_list_store_iface_setup, store);
173 empathy_contact_list_store_class_init (EmpathyContactListStoreClass *klass)
175 GObjectClass *object_class = G_OBJECT_CLASS (klass);
177 object_class->finalize = contact_list_store_finalize;
178 object_class->get_property = contact_list_store_get_property;
179 object_class->set_property = contact_list_store_set_property;
181 g_object_class_install_property (object_class,
183 g_param_spec_object ("contact-list",
184 "The contact list iface",
185 "The contact list iface",
186 EMPATHY_TYPE_CONTACT_LIST,
187 G_PARAM_CONSTRUCT_ONLY |
189 g_object_class_install_property (object_class,
191 g_param_spec_boolean ("show-offline",
193 "Whether contact list should display "
197 g_object_class_install_property (object_class,
199 g_param_spec_boolean ("show-avatars",
201 "Whether contact list should display "
202 "avatars for contacts",
205 g_object_class_install_property (object_class,
207 g_param_spec_boolean ("show-groups",
209 "Whether contact list should display "
213 g_object_class_install_property (object_class,
215 g_param_spec_boolean ("is-compact",
217 "Whether the contact list is in compact mode or not",
221 g_object_class_install_property (object_class,
223 g_param_spec_enum ("sort-criterium",
225 "The sort criterium to use for sorting the contact list",
226 EMPATHY_TYPE_CONTACT_LIST_STORE_SORT,
227 EMPATHY_CONTACT_LIST_STORE_SORT_NAME,
230 g_type_class_add_private (object_class, sizeof (EmpathyContactListStorePriv));
234 empathy_contact_list_store_init (EmpathyContactListStore *store)
236 EmpathyContactListStorePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (store,
237 EMPATHY_TYPE_CONTACT_LIST_STORE, EmpathyContactListStorePriv);
240 priv->show_avatars = TRUE;
241 priv->show_groups = TRUE;
242 priv->inhibit_active = g_timeout_add_seconds (ACTIVE_USER_WAIT_TO_ENABLE_TIME,
243 (GSourceFunc) contact_list_store_inibit_active_cb,
245 contact_list_store_setup (store);
249 contact_list_store_finalize (GObject *object)
251 EmpathyContactListStorePriv *priv = GET_PRIV (object);
253 g_signal_handlers_disconnect_by_func (priv->list,
254 G_CALLBACK (contact_list_store_groups_changed_cb),
256 g_object_unref (priv->list);
258 if (priv->inhibit_active) {
259 g_source_remove (priv->inhibit_active);
262 G_OBJECT_CLASS (empathy_contact_list_store_parent_class)->finalize (object);
266 contact_list_store_get_property (GObject *object,
271 EmpathyContactListStorePriv *priv;
273 priv = GET_PRIV (object);
276 case PROP_CONTACT_LIST:
277 g_value_set_object (value, priv->list);
279 case PROP_SHOW_OFFLINE:
280 g_value_set_boolean (value, priv->show_offline);
282 case PROP_SHOW_AVATARS:
283 g_value_set_boolean (value, priv->show_avatars);
285 case PROP_SHOW_GROUPS:
286 g_value_set_boolean (value, priv->show_groups);
288 case PROP_IS_COMPACT:
289 g_value_set_boolean (value, priv->is_compact);
291 case PROP_SORT_CRITERIUM:
292 g_value_set_enum (value, priv->sort_criterium);
295 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
301 contact_list_store_set_property (GObject *object,
306 EmpathyContactListStorePriv *priv;
308 priv = GET_PRIV (object);
311 case PROP_CONTACT_LIST:
312 contact_list_store_set_contact_list (EMPATHY_CONTACT_LIST_STORE (object),
313 g_value_get_object (value));
315 case PROP_SHOW_OFFLINE:
316 empathy_contact_list_store_set_show_offline (EMPATHY_CONTACT_LIST_STORE (object),
317 g_value_get_boolean (value));
319 case PROP_SHOW_AVATARS:
320 empathy_contact_list_store_set_show_avatars (EMPATHY_CONTACT_LIST_STORE (object),
321 g_value_get_boolean (value));
323 case PROP_SHOW_GROUPS:
324 empathy_contact_list_store_set_show_groups (EMPATHY_CONTACT_LIST_STORE (object),
325 g_value_get_boolean (value));
327 case PROP_IS_COMPACT:
328 empathy_contact_list_store_set_is_compact (EMPATHY_CONTACT_LIST_STORE (object),
329 g_value_get_boolean (value));
331 case PROP_SORT_CRITERIUM:
332 empathy_contact_list_store_set_sort_criterium (EMPATHY_CONTACT_LIST_STORE (object),
333 g_value_get_enum (value));
336 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
341 EmpathyContactListStore *
342 empathy_contact_list_store_new (EmpathyContactList *list_iface)
344 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list_iface), NULL);
346 return g_object_new (EMPATHY_TYPE_CONTACT_LIST_STORE,
347 "contact-list", list_iface,
352 empathy_contact_list_store_get_list_iface (EmpathyContactListStore *store)
354 EmpathyContactListStorePriv *priv;
356 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), FALSE);
358 priv = GET_PRIV (store);
364 empathy_contact_list_store_get_show_offline (EmpathyContactListStore *store)
366 EmpathyContactListStorePriv *priv;
368 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), FALSE);
370 priv = GET_PRIV (store);
372 return priv->show_offline;
376 empathy_contact_list_store_set_show_offline (EmpathyContactListStore *store,
377 gboolean show_offline)
379 EmpathyContactListStorePriv *priv;
381 gboolean show_active;
383 g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store));
385 priv = GET_PRIV (store);
387 priv->show_offline = show_offline;
388 show_active = priv->show_active;
390 /* Disable temporarily. */
391 priv->show_active = FALSE;
393 contacts = empathy_contact_list_get_members (priv->list);
394 for (l = contacts; l; l = l->next) {
395 contact_list_store_contact_update (store, l->data);
397 g_object_unref (l->data);
399 g_list_free (contacts);
401 /* Restore to original setting. */
402 priv->show_active = show_active;
404 g_object_notify (G_OBJECT (store), "show-offline");
408 empathy_contact_list_store_get_show_avatars (EmpathyContactListStore *store)
410 EmpathyContactListStorePriv *priv;
412 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), TRUE);
414 priv = GET_PRIV (store);
416 return priv->show_avatars;
420 empathy_contact_list_store_set_show_avatars (EmpathyContactListStore *store,
421 gboolean show_avatars)
423 EmpathyContactListStorePriv *priv;
426 g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store));
428 priv = GET_PRIV (store);
430 priv->show_avatars = show_avatars;
432 model = GTK_TREE_MODEL (store);
434 gtk_tree_model_foreach (model,
435 (GtkTreeModelForeachFunc)
436 contact_list_store_update_list_mode_foreach,
439 g_object_notify (G_OBJECT (store), "show-avatars");
443 empathy_contact_list_store_get_show_groups (EmpathyContactListStore *store)
445 EmpathyContactListStorePriv *priv;
447 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), TRUE);
449 priv = GET_PRIV (store);
451 return priv->show_groups;
455 empathy_contact_list_store_set_show_groups (EmpathyContactListStore *store,
456 gboolean show_groups)
458 EmpathyContactListStorePriv *priv;
461 g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store));
463 priv = GET_PRIV (store);
465 if (priv->show_groups == show_groups) {
469 priv->show_groups = show_groups;
471 /* Remove all contacts and add them back, not optimized but that's the
473 gtk_tree_store_clear (GTK_TREE_STORE (store));
474 contacts = empathy_contact_list_get_members (priv->list);
475 for (l = contacts; l; l = l->next) {
476 contact_monitor_contact_added_cb (empathy_contact_list_get_monitor (priv->list),
479 g_object_unref (l->data);
481 g_list_free (contacts);
483 g_object_notify (G_OBJECT (store), "show-groups");
487 empathy_contact_list_store_get_is_compact (EmpathyContactListStore *store)
489 EmpathyContactListStorePriv *priv;
491 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), TRUE);
493 priv = GET_PRIV (store);
495 return priv->is_compact;
499 empathy_contact_list_store_set_is_compact (EmpathyContactListStore *store,
502 EmpathyContactListStorePriv *priv;
505 g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store));
507 priv = GET_PRIV (store);
509 priv->is_compact = is_compact;
511 model = GTK_TREE_MODEL (store);
513 gtk_tree_model_foreach (model,
514 (GtkTreeModelForeachFunc)
515 contact_list_store_update_list_mode_foreach,
518 g_object_notify (G_OBJECT (store), "is-compact");
521 EmpathyContactListStoreSort
522 empathy_contact_list_store_get_sort_criterium (EmpathyContactListStore *store)
524 EmpathyContactListStorePriv *priv;
526 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), 0);
528 priv = GET_PRIV (store);
530 return priv->sort_criterium;
534 empathy_contact_list_store_set_sort_criterium (EmpathyContactListStore *store,
535 EmpathyContactListStoreSort sort_criterium)
537 EmpathyContactListStorePriv *priv;
539 g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store));
541 priv = GET_PRIV (store);
543 priv->sort_criterium = sort_criterium;
545 switch (sort_criterium) {
546 case EMPATHY_CONTACT_LIST_STORE_SORT_STATE:
547 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
548 EMPATHY_CONTACT_LIST_STORE_COL_STATUS,
552 case EMPATHY_CONTACT_LIST_STORE_SORT_NAME:
553 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
554 EMPATHY_CONTACT_LIST_STORE_COL_NAME,
559 g_object_notify (G_OBJECT (store), "sort-criterium");
563 empathy_contact_list_store_row_separator_func (GtkTreeModel *model,
567 gboolean is_separator = FALSE;
569 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
571 gtk_tree_model_get (model, iter,
572 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator,
579 empathy_contact_list_store_get_parent_group (GtkTreeModel *model,
581 gboolean *path_is_group)
583 GtkTreeIter parent_iter, iter;
587 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
590 *path_is_group = FALSE;
593 if (!gtk_tree_model_get_iter (model, &iter, path)) {
597 gtk_tree_model_get (model, &iter,
598 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
599 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
606 if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter)) {
612 gtk_tree_model_get (model, &iter,
613 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
614 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
623 *path_is_group = TRUE;
630 empathy_contact_list_store_search_equal_func (GtkTreeModel *model,
634 gpointer search_data)
636 gchar *name, *name_folded;
640 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
646 gtk_tree_model_get (model, iter,
647 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
654 name_folded = g_utf8_casefold (name, -1);
655 key_folded = g_utf8_casefold (key, -1);
657 if (name_folded && key_folded &&
658 strstr (name_folded, key_folded)) {
665 g_free (name_folded);
672 contact_list_store_setup (EmpathyContactListStore *store)
674 EmpathyContactListStorePriv *priv;
675 GType types[] = {G_TYPE_STRING, /* Status icon-name */
676 GDK_TYPE_PIXBUF, /* Avatar pixbuf */
677 G_TYPE_BOOLEAN, /* Avatar pixbuf visible */
678 G_TYPE_STRING, /* Name */
679 G_TYPE_STRING, /* Status string */
680 G_TYPE_BOOLEAN, /* Show status */
681 EMPATHY_TYPE_CONTACT, /* Contact type */
682 G_TYPE_BOOLEAN, /* Is group */
683 G_TYPE_BOOLEAN, /* Is active */
684 G_TYPE_BOOLEAN, /* Is online */
685 G_TYPE_BOOLEAN, /* Is separator */
686 G_TYPE_BOOLEAN}; /* Can VoIP */
688 priv = GET_PRIV (store);
690 gtk_tree_store_set_column_types (GTK_TREE_STORE (store),
691 EMPATHY_CONTACT_LIST_STORE_COL_COUNT,
695 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
696 EMPATHY_CONTACT_LIST_STORE_COL_NAME,
697 contact_list_store_name_sort_func,
699 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
700 EMPATHY_CONTACT_LIST_STORE_COL_STATUS,
701 contact_list_store_state_sort_func,
704 priv->sort_criterium = EMPATHY_CONTACT_LIST_STORE_SORT_NAME;
705 empathy_contact_list_store_set_sort_criterium (store, priv->sort_criterium);
709 contact_list_store_inibit_active_cb (EmpathyContactListStore *store)
711 EmpathyContactListStorePriv *priv;
713 priv = GET_PRIV (store);
715 priv->show_active = TRUE;
716 priv->inhibit_active = 0;
722 contact_list_store_groups_changed_cb (EmpathyContactList *list_iface,
723 EmpathyContact *contact,
726 EmpathyContactListStore *store)
728 EmpathyContactListStorePriv *priv;
729 gboolean show_active;
731 priv = GET_PRIV (store);
733 DEBUG ("Updating groups for contact %s (%d)",
734 empathy_contact_get_id (contact),
735 empathy_contact_get_handle (contact));
737 /* We do this to make sure the groups are correct, if not, we
738 * would have to check the groups already set up for each
739 * contact and then see what has been updated.
741 show_active = priv->show_active;
742 priv->show_active = FALSE;
743 contact_list_store_remove_contact (store, contact);
744 contact_list_store_add_contact (store, contact);
745 priv->show_active = show_active;
749 contact_list_store_add_contact (EmpathyContactListStore *store,
750 EmpathyContact *contact)
752 EmpathyContactListStorePriv *priv;
754 GList *groups = NULL, *l;
756 priv = GET_PRIV (store);
758 if (G_STR_EMPTY (empathy_contact_get_name (contact)) ||
759 (!priv->show_offline && !empathy_contact_is_online (contact))) {
763 if (priv->show_groups) {
764 groups = empathy_contact_list_get_groups (priv->list, contact);
767 /* If no groups just add it at the top level. */
769 gtk_tree_store_append (GTK_TREE_STORE (store), &iter, NULL);
770 gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
771 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact,
772 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE,
773 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE,
774 EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact),
778 /* Else add to each group. */
779 for (l = groups; l; l = l->next) {
780 GtkTreeIter iter_group;
782 contact_list_store_get_group (store, l->data, &iter_group, NULL, NULL);
784 gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter,
786 gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
787 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact,
788 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE,
789 EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact),
793 g_list_free (groups);
795 contact_list_store_contact_update (store, contact);
799 contact_list_store_remove_contact (EmpathyContactListStore *store,
800 EmpathyContact *contact)
802 EmpathyContactListStorePriv *priv;
806 priv = GET_PRIV (store);
808 iters = contact_list_store_find_contact (store, contact);
814 model = GTK_TREE_MODEL (store);
816 for (l = iters; l; l = l->next) {
819 /* NOTE: it is only <= 2 here because we have
820 * separators after the group name, otherwise it
823 if (gtk_tree_model_iter_parent (model, &parent, l->data) &&
824 gtk_tree_model_iter_n_children (model, &parent) <= 2) {
825 gtk_tree_store_remove (GTK_TREE_STORE (store), &parent);
827 gtk_tree_store_remove (GTK_TREE_STORE (store), l->data);
831 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
836 contact_monitor_presence_changed_cb (EmpathyContactMonitor *monitor,
837 EmpathyContact *contact,
840 EmpathyContactListStore *store)
842 EmpathyContactListStorePriv *priv;
844 ShowActiveData *data;
845 gboolean in_list, should_be_in_list;
846 gboolean do_remove = FALSE, do_set_active = FALSE,
847 do_set_refresh = FALSE, set_model = FALSE;
849 priv = GET_PRIV (store);
850 iters = contact_list_store_find_contact (store, contact);
852 in_list = (iters != NULL);
853 should_be_in_list = (current > MC_PRESENCE_OFFLINE ||
856 if (!in_list && !should_be_in_list) {
858 DEBUG ("Contact:'%s' in list:NO, should be:NO",
859 empathy_contact_get_name (contact));
861 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
864 } else if (in_list && !should_be_in_list) {
865 DEBUG ("Contact:'%s' in list:YES, should be:NO",
866 empathy_contact_get_name (contact));
868 if (priv->show_active) {
870 do_set_active = TRUE;
871 do_set_refresh = TRUE;
874 DEBUG ("Remove item (after timeout)");
876 DEBUG ("Remove item (now)!");
877 contact_list_store_remove_contact (store, contact);
879 } else if (!in_list && should_be_in_list) {
880 DEBUG ("Contact:'%s' in list:NO, should be:YES",
881 empathy_contact_get_name (contact));
883 contact_list_store_add_contact (store, contact);
885 if (priv->show_active) {
886 do_set_active = TRUE;
888 DEBUG ("Set active (contact added)");
891 DEBUG ("Contact:'%s' in list:YES, should be:YES",
892 empathy_contact_get_name (contact));
894 do_set_active = TRUE;
895 do_set_refresh = TRUE;
899 for (l = iters; l && set_model; l = l->next) {
900 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
901 EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, empathy_icon_name_for_contact (contact),
902 EMPATHY_CONTACT_LIST_STORE_COL_IS_ONLINE, current > MC_PRESENCE_OFFLINE,
903 EMPATHY_CONTACT_LIST_STORE_COL_STATUS, empathy_contact_get_status (contact),
907 if (priv->show_active && do_set_active) {
908 contact_list_store_contact_set_active (store, contact, do_set_active, do_set_refresh);
911 data = contact_list_store_contact_active_new (store, contact, do_remove);
912 g_timeout_add_seconds (ACTIVE_USER_SHOW_TIME,
913 (GSourceFunc) contact_list_store_contact_active_cb,
918 /* FIXME: when someone goes online then offline quickly, the
919 * first timeout sets the user to be inactive and the second
920 * timeout removes the user from the contact list, really we
921 * should remove the first timeout.
923 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
928 contact_monitor_presence_message_changed_cb (EmpathyContactMonitor *monitor,
929 EmpathyContact *contact,
931 EmpathyContactListStore *store)
933 EmpathyContactListStorePriv *priv;
936 priv = GET_PRIV (store);
937 iters = contact_list_store_find_contact (store, contact);
939 for (l = iters; l; l = l->next) {
940 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
941 EMPATHY_CONTACT_LIST_STORE_COL_STATUS, message,
942 EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, !priv->is_compact,
946 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
951 contact_monitor_capabilities_changed_cb (EmpathyContactMonitor *monitor,
952 EmpathyContact *contact,
953 EmpathyContactListStore *store)
957 iters = contact_list_store_find_contact (store, contact);
963 for (l = iters; l; l = l->next) {
964 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
965 EMPATHY_CONTACT_LIST_STORE_COL_CAN_VOIP, empathy_contact_can_voip (contact),
969 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
974 contact_monitor_avatar_changed_cb (EmpathyContactMonitor *monitor,
975 EmpathyContact *contact,
976 EmpathyContactListStore *store)
978 EmpathyContactListStorePriv *priv;
979 GdkPixbuf *pixbuf_avatar;
981 gboolean show_avatar = FALSE;
983 priv = GET_PRIV (store);
985 iters = contact_list_store_find_contact (store, contact);
991 if (priv->show_avatars && !priv->is_compact) {
995 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact, 32, 32);
997 for (l = iters; l; l = l->next) {
998 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
999 EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR, pixbuf_avatar,
1000 EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
1004 if (pixbuf_avatar) {
1005 g_object_unref (pixbuf_avatar);
1008 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
1009 g_list_free (iters);
1013 contact_monitor_name_changed_cb (EmpathyContactMonitor *monitor,
1014 EmpathyContact *contact,
1016 EmpathyContactListStore *store)
1020 iters = contact_list_store_find_contact (store, contact);
1026 for (l = iters; l; l = l->next) {
1027 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
1028 EMPATHY_CONTACT_LIST_STORE_COL_NAME, name,
1032 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
1033 g_list_free (iters);
1037 contact_list_store_contact_update (EmpathyContactListStore *store,
1038 EmpathyContact *contact)
1040 contact_monitor_name_changed_cb (NULL, contact,
1041 empathy_contact_get_name (contact),
1043 contact_monitor_presence_changed_cb (NULL, contact,
1044 empathy_contact_get_presence (contact),
1047 contact_monitor_presence_message_changed_cb (NULL, contact,
1048 empathy_contact_get_status (contact),
1050 contact_monitor_avatar_changed_cb (NULL, contact, store);
1051 contact_monitor_capabilities_changed_cb (NULL, contact, store);
1055 contact_monitor_contact_added_cb (EmpathyContactMonitor *monitor,
1056 EmpathyContact *contact,
1057 EmpathyContactListStore *store)
1059 contact_list_store_add_contact (store, contact);
1063 contact_monitor_contact_removed_cb (EmpathyContactMonitor *monitor,
1064 EmpathyContact *contact,
1065 EmpathyContactListStore *store)
1067 contact_list_store_remove_contact (store, contact);
1071 contact_list_store_iface_setup (gpointer user_data)
1073 EmpathyContactListStore *store = user_data;
1074 EmpathyContactListStorePriv *priv = GET_PRIV (store);
1075 EmpathyContactMonitor *monitor;
1076 GList *contacts, *l;
1078 monitor = empathy_contact_list_get_monitor (priv->list);
1080 /* Signal connection. */
1081 g_signal_connect (monitor, "contact-added",
1082 G_CALLBACK (contact_monitor_contact_added_cb), store);
1083 g_signal_connect (monitor, "contact-removed",
1084 G_CALLBACK (contact_monitor_contact_removed_cb), store);
1085 g_signal_connect (monitor, "contact-presence-changed",
1086 G_CALLBACK (contact_monitor_presence_changed_cb), store);
1087 g_signal_connect (monitor, "contact-presence-message-changed",
1088 G_CALLBACK (contact_monitor_presence_message_changed_cb), store);
1089 g_signal_connect (monitor, "contact-name-changed",
1090 G_CALLBACK (contact_monitor_name_changed_cb), store);
1091 g_signal_connect (monitor, "contact-capabilities-changed",
1092 G_CALLBACK (contact_monitor_capabilities_changed_cb), store);
1093 g_signal_connect (monitor, "contact-avatar-changed",
1094 G_CALLBACK (contact_monitor_avatar_changed_cb), store);
1096 /* Add contacts already created. */
1097 contacts = empathy_contact_list_get_members (priv->list);
1098 for (l = contacts; l; l = l->next) {
1099 contact_monitor_contact_added_cb (monitor, l->data, store);
1101 g_object_unref (l->data);
1103 g_list_free (contacts);
1109 contact_list_store_contact_set_active (EmpathyContactListStore *store,
1110 EmpathyContact *contact,
1112 gboolean set_changed)
1114 EmpathyContactListStorePriv *priv;
1115 GtkTreeModel *model;
1118 priv = GET_PRIV (store);
1119 model = GTK_TREE_MODEL (store);
1121 iters = contact_list_store_find_contact (store, contact);
1122 for (l = iters; l; l = l->next) {
1125 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
1126 EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, active,
1129 DEBUG ("Set item %s", active ? "active" : "inactive");
1132 path = gtk_tree_model_get_path (model, l->data);
1133 gtk_tree_model_row_changed (model, path, l->data);
1134 gtk_tree_path_free (path);
1138 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
1139 g_list_free (iters);
1143 static ShowActiveData *
1144 contact_list_store_contact_active_new (EmpathyContactListStore *store,
1145 EmpathyContact *contact,
1148 ShowActiveData *data;
1150 DEBUG ("Contact:'%s' now active, and %s be removed",
1151 empathy_contact_get_name (contact),
1152 remove ? "WILL" : "WILL NOT");
1154 data = g_slice_new0 (ShowActiveData);
1156 data->store = g_object_ref (store);
1157 data->contact = g_object_ref (contact);
1158 data->remove = remove;
1164 contact_list_store_contact_active_free (ShowActiveData *data)
1166 g_object_unref (data->contact);
1167 g_object_unref (data->store);
1169 g_slice_free (ShowActiveData, data);
1173 contact_list_store_contact_active_cb (ShowActiveData *data)
1175 EmpathyContactListStorePriv *priv;
1177 priv = GET_PRIV (data->store);
1180 !priv->show_offline &&
1181 !empathy_contact_is_online (data->contact)) {
1182 DEBUG ("Contact:'%s' active timeout, removing item",
1183 empathy_contact_get_name (data->contact));
1184 contact_list_store_remove_contact (data->store, data->contact);
1187 DEBUG ("Contact:'%s' no longer active",
1188 empathy_contact_get_name (data->contact));
1190 contact_list_store_contact_set_active (data->store,
1195 contact_list_store_contact_active_free (data);
1201 contact_list_store_get_group_foreach (GtkTreeModel *model,
1209 /* Groups are only at the top level. */
1210 if (gtk_tree_path_get_depth (path) != 1) {
1214 gtk_tree_model_get (model, iter,
1215 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &str,
1216 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1219 if (is_group && !tp_strdiff (str, fg->name)) {
1230 contact_list_store_get_group (EmpathyContactListStore *store,
1232 GtkTreeIter *iter_group_to_set,
1233 GtkTreeIter *iter_separator_to_set,
1236 EmpathyContactListStorePriv *priv;
1237 GtkTreeModel *model;
1238 GtkTreeIter iter_group;
1239 GtkTreeIter iter_separator;
1242 priv = GET_PRIV (store);
1244 memset (&fg, 0, sizeof (fg));
1248 model = GTK_TREE_MODEL (store);
1249 gtk_tree_model_foreach (model,
1250 (GtkTreeModelForeachFunc) contact_list_store_get_group_foreach,
1258 gtk_tree_store_append (GTK_TREE_STORE (store), &iter_group, NULL);
1259 gtk_tree_store_set (GTK_TREE_STORE (store), &iter_group,
1260 EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, NULL,
1261 EMPATHY_CONTACT_LIST_STORE_COL_NAME, name,
1262 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, TRUE,
1263 EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, FALSE,
1264 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE,
1267 if (iter_group_to_set) {
1268 *iter_group_to_set = iter_group;
1271 gtk_tree_store_append (GTK_TREE_STORE (store),
1274 gtk_tree_store_set (GTK_TREE_STORE (store), &iter_separator,
1275 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, TRUE,
1278 if (iter_separator_to_set) {
1279 *iter_separator_to_set = iter_separator;
1286 if (iter_group_to_set) {
1287 *iter_group_to_set = fg.iter;
1290 iter_separator = fg.iter;
1292 if (gtk_tree_model_iter_next (model, &iter_separator)) {
1293 gboolean is_separator;
1295 gtk_tree_model_get (model, &iter_separator,
1296 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator,
1299 if (is_separator && iter_separator_to_set) {
1300 *iter_separator_to_set = iter_separator;
1307 contact_list_store_ordered_presence (McPresence state)
1310 case MC_PRESENCE_UNSET:
1311 case MC_PRESENCE_OFFLINE:
1313 case MC_PRESENCE_AVAILABLE:
1315 case MC_PRESENCE_AWAY:
1317 case MC_PRESENCE_EXTENDED_AWAY:
1319 case MC_PRESENCE_HIDDEN:
1321 case MC_PRESENCE_DO_NOT_DISTURB:
1324 g_return_val_if_reached (6);
1329 contact_list_store_state_sort_func (GtkTreeModel *model,
1330 GtkTreeIter *iter_a,
1331 GtkTreeIter *iter_b,
1335 gchar *name_a, *name_b;
1336 gboolean is_separator_a, is_separator_b;
1337 EmpathyContact *contact_a, *contact_b;
1338 guint presence_a, presence_b;
1340 gtk_tree_model_get (model, iter_a,
1341 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_a,
1342 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_a,
1343 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_a,
1345 gtk_tree_model_get (model, iter_b,
1346 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_b,
1347 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_b,
1348 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_b,
1351 /* Separator or group? */
1352 if (is_separator_a || is_separator_b) {
1353 if (is_separator_a) {
1355 } else if (is_separator_b) {
1358 } else if (!contact_a && contact_b) {
1360 } else if (contact_a && !contact_b) {
1362 } else if (!contact_a && !contact_b) {
1364 ret_val = g_utf8_collate (name_a, name_b);
1371 /* If we managed to get this far, we can start looking at
1374 presence_a = empathy_contact_get_presence (EMPATHY_CONTACT (contact_a));
1375 presence_a = contact_list_store_ordered_presence (presence_a);
1376 presence_b = empathy_contact_get_presence (EMPATHY_CONTACT (contact_b));
1377 presence_b = contact_list_store_ordered_presence (presence_b);
1379 if (presence_a < presence_b) {
1381 } else if (presence_a > presence_b) {
1384 /* Fallback: compare by name */
1385 ret_val = g_utf8_collate (name_a, name_b);
1393 g_object_unref (contact_a);
1397 g_object_unref (contact_b);
1404 contact_list_store_name_sort_func (GtkTreeModel *model,
1405 GtkTreeIter *iter_a,
1406 GtkTreeIter *iter_b,
1409 gchar *name_a, *name_b;
1410 EmpathyContact *contact_a, *contact_b;
1411 gboolean is_separator_a, is_separator_b;
1414 gtk_tree_model_get (model, iter_a,
1415 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_a,
1416 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_a,
1417 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_a,
1419 gtk_tree_model_get (model, iter_b,
1420 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_b,
1421 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_b,
1422 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_b,
1425 /* If contact is NULL it means it's a group. */
1427 if (is_separator_a || is_separator_b) {
1428 if (is_separator_a) {
1430 } else if (is_separator_b) {
1433 } else if (!contact_a && contact_b) {
1435 } else if (contact_a && !contact_b) {
1438 ret_val = g_utf8_collate (name_a, name_b);
1445 g_object_unref (contact_a);
1449 g_object_unref (contact_b);
1456 contact_list_store_find_contact_foreach (GtkTreeModel *model,
1461 EmpathyContact *contact;
1463 gtk_tree_model_get (model, iter,
1464 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
1467 if (contact == fc->contact) {
1469 fc->iters = g_list_append (fc->iters, gtk_tree_iter_copy (iter));
1473 g_object_unref (contact);
1480 contact_list_store_find_contact (EmpathyContactListStore *store,
1481 EmpathyContact *contact)
1483 EmpathyContactListStorePriv *priv;
1484 GtkTreeModel *model;
1488 priv = GET_PRIV (store);
1490 memset (&fc, 0, sizeof (fc));
1492 fc.contact = contact;
1494 model = GTK_TREE_MODEL (store);
1495 gtk_tree_model_foreach (model,
1496 (GtkTreeModelForeachFunc) contact_list_store_find_contact_foreach,
1507 contact_list_store_update_list_mode_foreach (GtkTreeModel *model,
1510 EmpathyContactListStore *store)
1512 EmpathyContactListStorePriv *priv;
1513 gboolean show_avatar = FALSE;
1515 priv = GET_PRIV (store);
1517 if (priv->show_avatars && !priv->is_compact) {
1521 gtk_tree_store_set (GTK_TREE_STORE (store), iter,
1522 EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
1523 EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, !priv->is_compact,