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);
146 static void disconnect_monitor_signals (EmpathyContactMonitor *monitor,
159 G_DEFINE_TYPE (EmpathyContactListStore, empathy_contact_list_store, GTK_TYPE_TREE_STORE);
163 contact_list_store_set_contact_list (EmpathyContactListStore *store,
164 EmpathyContactList *list_iface)
166 EmpathyContactListStorePriv *priv = GET_PRIV (store);
168 priv->list = g_object_ref (list_iface);
170 /* Let a chance to have all properties set before populating */
171 g_idle_add (contact_list_store_iface_setup, store);
175 empathy_contact_list_store_class_init (EmpathyContactListStoreClass *klass)
177 GObjectClass *object_class = G_OBJECT_CLASS (klass);
179 object_class->finalize = contact_list_store_finalize;
180 object_class->get_property = contact_list_store_get_property;
181 object_class->set_property = contact_list_store_set_property;
183 g_object_class_install_property (object_class,
185 g_param_spec_object ("contact-list",
186 "The contact list iface",
187 "The contact list iface",
188 EMPATHY_TYPE_CONTACT_LIST,
189 G_PARAM_CONSTRUCT_ONLY |
191 g_object_class_install_property (object_class,
193 g_param_spec_boolean ("show-offline",
195 "Whether contact list should display "
199 g_object_class_install_property (object_class,
201 g_param_spec_boolean ("show-avatars",
203 "Whether contact list should display "
204 "avatars for contacts",
207 g_object_class_install_property (object_class,
209 g_param_spec_boolean ("show-groups",
211 "Whether contact list should display "
215 g_object_class_install_property (object_class,
217 g_param_spec_boolean ("is-compact",
219 "Whether the contact list is in compact mode or not",
223 g_object_class_install_property (object_class,
225 g_param_spec_enum ("sort-criterium",
227 "The sort criterium to use for sorting the contact list",
228 EMPATHY_TYPE_CONTACT_LIST_STORE_SORT,
229 EMPATHY_CONTACT_LIST_STORE_SORT_NAME,
232 g_type_class_add_private (object_class, sizeof (EmpathyContactListStorePriv));
236 empathy_contact_list_store_init (EmpathyContactListStore *store)
238 EmpathyContactListStorePriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (store,
239 EMPATHY_TYPE_CONTACT_LIST_STORE, EmpathyContactListStorePriv);
242 priv->show_avatars = TRUE;
243 priv->show_groups = TRUE;
244 priv->inhibit_active = g_timeout_add_seconds (ACTIVE_USER_WAIT_TO_ENABLE_TIME,
245 (GSourceFunc) contact_list_store_inibit_active_cb,
247 contact_list_store_setup (store);
251 contact_list_store_finalize (GObject *object)
253 EmpathyContactListStorePriv *priv = GET_PRIV (object);
254 EmpathyContactMonitor *monitor;
256 monitor = empathy_contact_list_get_monitor (priv->list);
257 disconnect_monitor_signals (monitor, object);
259 g_signal_handlers_disconnect_by_func (priv->list,
260 G_CALLBACK (contact_list_store_groups_changed_cb),
262 g_object_unref (priv->list);
264 if (priv->inhibit_active) {
265 g_source_remove (priv->inhibit_active);
268 G_OBJECT_CLASS (empathy_contact_list_store_parent_class)->finalize (object);
272 contact_list_store_get_property (GObject *object,
277 EmpathyContactListStorePriv *priv;
279 priv = GET_PRIV (object);
282 case PROP_CONTACT_LIST:
283 g_value_set_object (value, priv->list);
285 case PROP_SHOW_OFFLINE:
286 g_value_set_boolean (value, priv->show_offline);
288 case PROP_SHOW_AVATARS:
289 g_value_set_boolean (value, priv->show_avatars);
291 case PROP_SHOW_GROUPS:
292 g_value_set_boolean (value, priv->show_groups);
294 case PROP_IS_COMPACT:
295 g_value_set_boolean (value, priv->is_compact);
297 case PROP_SORT_CRITERIUM:
298 g_value_set_enum (value, priv->sort_criterium);
301 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
307 contact_list_store_set_property (GObject *object,
312 EmpathyContactListStorePriv *priv;
314 priv = GET_PRIV (object);
317 case PROP_CONTACT_LIST:
318 contact_list_store_set_contact_list (EMPATHY_CONTACT_LIST_STORE (object),
319 g_value_get_object (value));
321 case PROP_SHOW_OFFLINE:
322 empathy_contact_list_store_set_show_offline (EMPATHY_CONTACT_LIST_STORE (object),
323 g_value_get_boolean (value));
325 case PROP_SHOW_AVATARS:
326 empathy_contact_list_store_set_show_avatars (EMPATHY_CONTACT_LIST_STORE (object),
327 g_value_get_boolean (value));
329 case PROP_SHOW_GROUPS:
330 empathy_contact_list_store_set_show_groups (EMPATHY_CONTACT_LIST_STORE (object),
331 g_value_get_boolean (value));
333 case PROP_IS_COMPACT:
334 empathy_contact_list_store_set_is_compact (EMPATHY_CONTACT_LIST_STORE (object),
335 g_value_get_boolean (value));
337 case PROP_SORT_CRITERIUM:
338 empathy_contact_list_store_set_sort_criterium (EMPATHY_CONTACT_LIST_STORE (object),
339 g_value_get_enum (value));
342 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
347 EmpathyContactListStore *
348 empathy_contact_list_store_new (EmpathyContactList *list_iface)
350 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST (list_iface), NULL);
352 return g_object_new (EMPATHY_TYPE_CONTACT_LIST_STORE,
353 "contact-list", list_iface,
358 empathy_contact_list_store_get_list_iface (EmpathyContactListStore *store)
360 EmpathyContactListStorePriv *priv;
362 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), FALSE);
364 priv = GET_PRIV (store);
370 empathy_contact_list_store_get_show_offline (EmpathyContactListStore *store)
372 EmpathyContactListStorePriv *priv;
374 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), FALSE);
376 priv = GET_PRIV (store);
378 return priv->show_offline;
382 empathy_contact_list_store_set_show_offline (EmpathyContactListStore *store,
383 gboolean show_offline)
385 EmpathyContactListStorePriv *priv;
387 gboolean show_active;
389 g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store));
391 priv = GET_PRIV (store);
393 priv->show_offline = show_offline;
394 show_active = priv->show_active;
396 /* Disable temporarily. */
397 priv->show_active = FALSE;
399 contacts = empathy_contact_list_get_members (priv->list);
400 for (l = contacts; l; l = l->next) {
401 contact_list_store_contact_update (store, l->data);
403 g_object_unref (l->data);
405 g_list_free (contacts);
407 /* Restore to original setting. */
408 priv->show_active = show_active;
410 g_object_notify (G_OBJECT (store), "show-offline");
414 empathy_contact_list_store_get_show_avatars (EmpathyContactListStore *store)
416 EmpathyContactListStorePriv *priv;
418 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), TRUE);
420 priv = GET_PRIV (store);
422 return priv->show_avatars;
426 empathy_contact_list_store_set_show_avatars (EmpathyContactListStore *store,
427 gboolean show_avatars)
429 EmpathyContactListStorePriv *priv;
432 g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store));
434 priv = GET_PRIV (store);
436 priv->show_avatars = show_avatars;
438 model = GTK_TREE_MODEL (store);
440 gtk_tree_model_foreach (model,
441 (GtkTreeModelForeachFunc)
442 contact_list_store_update_list_mode_foreach,
445 g_object_notify (G_OBJECT (store), "show-avatars");
449 empathy_contact_list_store_get_show_groups (EmpathyContactListStore *store)
451 EmpathyContactListStorePriv *priv;
453 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), TRUE);
455 priv = GET_PRIV (store);
457 return priv->show_groups;
461 empathy_contact_list_store_set_show_groups (EmpathyContactListStore *store,
462 gboolean show_groups)
464 EmpathyContactListStorePriv *priv;
467 g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store));
469 priv = GET_PRIV (store);
471 if (priv->show_groups == show_groups) {
475 priv->show_groups = show_groups;
477 /* Remove all contacts and add them back, not optimized but that's the
479 gtk_tree_store_clear (GTK_TREE_STORE (store));
480 contacts = empathy_contact_list_get_members (priv->list);
481 for (l = contacts; l; l = l->next) {
482 contact_monitor_contact_added_cb (empathy_contact_list_get_monitor (priv->list),
485 g_object_unref (l->data);
487 g_list_free (contacts);
489 g_object_notify (G_OBJECT (store), "show-groups");
493 empathy_contact_list_store_get_is_compact (EmpathyContactListStore *store)
495 EmpathyContactListStorePriv *priv;
497 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), TRUE);
499 priv = GET_PRIV (store);
501 return priv->is_compact;
505 empathy_contact_list_store_set_is_compact (EmpathyContactListStore *store,
508 EmpathyContactListStorePriv *priv;
511 g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store));
513 priv = GET_PRIV (store);
515 priv->is_compact = is_compact;
517 model = GTK_TREE_MODEL (store);
519 gtk_tree_model_foreach (model,
520 (GtkTreeModelForeachFunc)
521 contact_list_store_update_list_mode_foreach,
524 g_object_notify (G_OBJECT (store), "is-compact");
527 EmpathyContactListStoreSort
528 empathy_contact_list_store_get_sort_criterium (EmpathyContactListStore *store)
530 EmpathyContactListStorePriv *priv;
532 g_return_val_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store), 0);
534 priv = GET_PRIV (store);
536 return priv->sort_criterium;
540 empathy_contact_list_store_set_sort_criterium (EmpathyContactListStore *store,
541 EmpathyContactListStoreSort sort_criterium)
543 EmpathyContactListStorePriv *priv;
545 g_return_if_fail (EMPATHY_IS_CONTACT_LIST_STORE (store));
547 priv = GET_PRIV (store);
549 priv->sort_criterium = sort_criterium;
551 switch (sort_criterium) {
552 case EMPATHY_CONTACT_LIST_STORE_SORT_STATE:
553 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
554 EMPATHY_CONTACT_LIST_STORE_COL_STATUS,
558 case EMPATHY_CONTACT_LIST_STORE_SORT_NAME:
559 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
560 EMPATHY_CONTACT_LIST_STORE_COL_NAME,
565 g_object_notify (G_OBJECT (store), "sort-criterium");
569 empathy_contact_list_store_row_separator_func (GtkTreeModel *model,
573 gboolean is_separator = FALSE;
575 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
577 gtk_tree_model_get (model, iter,
578 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator,
585 empathy_contact_list_store_get_parent_group (GtkTreeModel *model,
587 gboolean *path_is_group)
589 GtkTreeIter parent_iter, iter;
593 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
596 *path_is_group = FALSE;
599 if (!gtk_tree_model_get_iter (model, &iter, path)) {
603 gtk_tree_model_get (model, &iter,
604 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
605 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
612 if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter)) {
618 gtk_tree_model_get (model, &iter,
619 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
620 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
629 *path_is_group = TRUE;
636 empathy_contact_list_store_search_equal_func (GtkTreeModel *model,
640 gpointer search_data)
642 gchar *name, *name_folded;
646 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
652 gtk_tree_model_get (model, iter,
653 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name,
660 name_folded = g_utf8_casefold (name, -1);
661 key_folded = g_utf8_casefold (key, -1);
663 if (name_folded && key_folded &&
664 strstr (name_folded, key_folded)) {
671 g_free (name_folded);
678 contact_list_store_setup (EmpathyContactListStore *store)
680 EmpathyContactListStorePriv *priv;
681 GType types[] = {G_TYPE_STRING, /* Status icon-name */
682 GDK_TYPE_PIXBUF, /* Avatar pixbuf */
683 G_TYPE_BOOLEAN, /* Avatar pixbuf visible */
684 G_TYPE_STRING, /* Name */
685 G_TYPE_STRING, /* Status string */
686 G_TYPE_BOOLEAN, /* Show status */
687 EMPATHY_TYPE_CONTACT, /* Contact type */
688 G_TYPE_BOOLEAN, /* Is group */
689 G_TYPE_BOOLEAN, /* Is active */
690 G_TYPE_BOOLEAN, /* Is online */
691 G_TYPE_BOOLEAN, /* Is separator */
692 G_TYPE_BOOLEAN}; /* Can VoIP */
694 priv = GET_PRIV (store);
696 gtk_tree_store_set_column_types (GTK_TREE_STORE (store),
697 EMPATHY_CONTACT_LIST_STORE_COL_COUNT,
701 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
702 EMPATHY_CONTACT_LIST_STORE_COL_NAME,
703 contact_list_store_name_sort_func,
705 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
706 EMPATHY_CONTACT_LIST_STORE_COL_STATUS,
707 contact_list_store_state_sort_func,
710 priv->sort_criterium = EMPATHY_CONTACT_LIST_STORE_SORT_NAME;
711 empathy_contact_list_store_set_sort_criterium (store, priv->sort_criterium);
715 contact_list_store_inibit_active_cb (EmpathyContactListStore *store)
717 EmpathyContactListStorePriv *priv;
719 priv = GET_PRIV (store);
721 priv->show_active = TRUE;
722 priv->inhibit_active = 0;
728 contact_list_store_groups_changed_cb (EmpathyContactList *list_iface,
729 EmpathyContact *contact,
732 EmpathyContactListStore *store)
734 EmpathyContactListStorePriv *priv;
735 gboolean show_active;
737 priv = GET_PRIV (store);
739 DEBUG ("Updating groups for contact %s (%d)",
740 empathy_contact_get_id (contact),
741 empathy_contact_get_handle (contact));
743 /* We do this to make sure the groups are correct, if not, we
744 * would have to check the groups already set up for each
745 * contact and then see what has been updated.
747 show_active = priv->show_active;
748 priv->show_active = FALSE;
749 contact_list_store_remove_contact (store, contact);
750 contact_list_store_add_contact (store, contact);
751 priv->show_active = show_active;
755 contact_list_store_add_contact (EmpathyContactListStore *store,
756 EmpathyContact *contact)
758 EmpathyContactListStorePriv *priv;
760 GList *groups = NULL, *l;
762 priv = GET_PRIV (store);
764 if (G_STR_EMPTY (empathy_contact_get_name (contact)) ||
765 (!priv->show_offline && !empathy_contact_is_online (contact))) {
769 if (priv->show_groups) {
770 groups = empathy_contact_list_get_groups (priv->list, contact);
773 /* If no groups just add it at the top level. */
775 gtk_tree_store_append (GTK_TREE_STORE (store), &iter, NULL);
776 gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
777 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact,
778 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE,
779 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE,
780 EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact),
784 /* Else add to each group. */
785 for (l = groups; l; l = l->next) {
786 GtkTreeIter iter_group;
788 contact_list_store_get_group (store, l->data, &iter_group, NULL, NULL);
790 gtk_tree_store_insert_after (GTK_TREE_STORE (store), &iter,
792 gtk_tree_store_set (GTK_TREE_STORE (store), &iter,
793 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, contact,
794 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, FALSE,
795 EMPATHY_CONTACT_LIST_STORE_COL_NAME, empathy_contact_get_name (contact),
799 g_list_free (groups);
801 contact_list_store_contact_update (store, contact);
805 contact_list_store_remove_contact (EmpathyContactListStore *store,
806 EmpathyContact *contact)
808 EmpathyContactListStorePriv *priv;
812 priv = GET_PRIV (store);
814 iters = contact_list_store_find_contact (store, contact);
820 model = GTK_TREE_MODEL (store);
822 for (l = iters; l; l = l->next) {
825 /* NOTE: it is only <= 2 here because we have
826 * separators after the group name, otherwise it
829 if (gtk_tree_model_iter_parent (model, &parent, l->data) &&
830 gtk_tree_model_iter_n_children (model, &parent) <= 2) {
831 gtk_tree_store_remove (GTK_TREE_STORE (store), &parent);
833 gtk_tree_store_remove (GTK_TREE_STORE (store), l->data);
837 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
842 contact_monitor_presence_changed_cb (EmpathyContactMonitor *monitor,
843 EmpathyContact *contact,
846 EmpathyContactListStore *store)
848 EmpathyContactListStorePriv *priv;
850 ShowActiveData *data;
851 gboolean in_list, should_be_in_list;
852 gboolean do_remove = FALSE, do_set_active = FALSE,
853 do_set_refresh = FALSE, set_model = FALSE;
855 priv = GET_PRIV (store);
856 iters = contact_list_store_find_contact (store, contact);
858 in_list = (iters != NULL);
859 should_be_in_list = (current > MC_PRESENCE_OFFLINE ||
862 if (!in_list && !should_be_in_list) {
864 DEBUG ("Contact:'%s' in list:NO, should be:NO",
865 empathy_contact_get_name (contact));
867 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
870 } else if (in_list && !should_be_in_list) {
871 DEBUG ("Contact:'%s' in list:YES, should be:NO",
872 empathy_contact_get_name (contact));
874 if (priv->show_active) {
876 do_set_active = TRUE;
877 do_set_refresh = TRUE;
880 DEBUG ("Remove item (after timeout)");
882 DEBUG ("Remove item (now)!");
883 contact_list_store_remove_contact (store, contact);
885 } else if (!in_list && should_be_in_list) {
886 DEBUG ("Contact:'%s' in list:NO, should be:YES",
887 empathy_contact_get_name (contact));
889 contact_list_store_add_contact (store, contact);
891 if (priv->show_active) {
892 do_set_active = TRUE;
894 DEBUG ("Set active (contact added)");
897 DEBUG ("Contact:'%s' in list:YES, should be:YES",
898 empathy_contact_get_name (contact));
900 do_set_active = TRUE;
901 do_set_refresh = TRUE;
905 for (l = iters; l && set_model; l = l->next) {
906 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
907 EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, empathy_icon_name_for_contact (contact),
908 EMPATHY_CONTACT_LIST_STORE_COL_IS_ONLINE, current > MC_PRESENCE_OFFLINE,
909 EMPATHY_CONTACT_LIST_STORE_COL_STATUS, empathy_contact_get_status (contact),
913 if (priv->show_active && do_set_active) {
914 contact_list_store_contact_set_active (store, contact, do_set_active, do_set_refresh);
917 data = contact_list_store_contact_active_new (store, contact, do_remove);
918 g_timeout_add_seconds (ACTIVE_USER_SHOW_TIME,
919 (GSourceFunc) contact_list_store_contact_active_cb,
924 /* FIXME: when someone goes online then offline quickly, the
925 * first timeout sets the user to be inactive and the second
926 * timeout removes the user from the contact list, really we
927 * should remove the first timeout.
929 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
934 contact_monitor_presence_message_changed_cb (EmpathyContactMonitor *monitor,
935 EmpathyContact *contact,
937 EmpathyContactListStore *store)
939 EmpathyContactListStorePriv *priv;
942 priv = GET_PRIV (store);
943 iters = contact_list_store_find_contact (store, contact);
945 for (l = iters; l; l = l->next) {
946 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
947 EMPATHY_CONTACT_LIST_STORE_COL_STATUS, message,
948 EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, !priv->is_compact,
952 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
957 contact_monitor_capabilities_changed_cb (EmpathyContactMonitor *monitor,
958 EmpathyContact *contact,
959 EmpathyContactListStore *store)
963 iters = contact_list_store_find_contact (store, contact);
969 for (l = iters; l; l = l->next) {
970 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
971 EMPATHY_CONTACT_LIST_STORE_COL_CAN_VOIP, empathy_contact_can_voip (contact),
975 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
980 contact_monitor_avatar_changed_cb (EmpathyContactMonitor *monitor,
981 EmpathyContact *contact,
982 EmpathyContactListStore *store)
984 EmpathyContactListStorePriv *priv;
985 GdkPixbuf *pixbuf_avatar;
987 gboolean show_avatar = FALSE;
989 priv = GET_PRIV (store);
991 iters = contact_list_store_find_contact (store, contact);
997 if (priv->show_avatars && !priv->is_compact) {
1001 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact, 32, 32);
1003 for (l = iters; l; l = l->next) {
1004 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
1005 EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR, pixbuf_avatar,
1006 EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
1010 if (pixbuf_avatar) {
1011 g_object_unref (pixbuf_avatar);
1014 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
1015 g_list_free (iters);
1019 contact_monitor_name_changed_cb (EmpathyContactMonitor *monitor,
1020 EmpathyContact *contact,
1022 EmpathyContactListStore *store)
1026 iters = contact_list_store_find_contact (store, contact);
1032 for (l = iters; l; l = l->next) {
1033 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
1034 EMPATHY_CONTACT_LIST_STORE_COL_NAME, name,
1038 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
1039 g_list_free (iters);
1043 contact_list_store_contact_update (EmpathyContactListStore *store,
1044 EmpathyContact *contact)
1046 contact_monitor_name_changed_cb (NULL, contact,
1047 empathy_contact_get_name (contact),
1049 contact_monitor_presence_changed_cb (NULL, contact,
1050 empathy_contact_get_presence (contact),
1053 contact_monitor_presence_message_changed_cb (NULL, contact,
1054 empathy_contact_get_status (contact),
1056 contact_monitor_avatar_changed_cb (NULL, contact, store);
1057 contact_monitor_capabilities_changed_cb (NULL, contact, store);
1061 contact_monitor_contact_added_cb (EmpathyContactMonitor *monitor,
1062 EmpathyContact *contact,
1063 EmpathyContactListStore *store)
1065 contact_list_store_add_contact (store, contact);
1069 contact_monitor_contact_removed_cb (EmpathyContactMonitor *monitor,
1070 EmpathyContact *contact,
1071 EmpathyContactListStore *store)
1073 contact_list_store_remove_contact (store, contact);
1078 disconnect_monitor_signals (EmpathyContactMonitor *monitor,
1081 g_signal_handlers_disconnect_by_func (monitor,
1082 contact_monitor_avatar_changed_cb, obj);
1083 g_signal_handlers_disconnect_by_func (monitor,
1084 contact_monitor_capabilities_changed_cb, obj);
1085 g_signal_handlers_disconnect_by_func (monitor,
1086 contact_monitor_contact_added_cb, obj);
1087 g_signal_handlers_disconnect_by_func (monitor,
1088 contact_monitor_contact_removed_cb, obj);
1089 g_signal_handlers_disconnect_by_func (monitor,
1090 contact_monitor_name_changed_cb, obj);
1091 g_signal_handlers_disconnect_by_func (monitor,
1092 contact_monitor_presence_changed_cb, obj);
1093 g_signal_handlers_disconnect_by_func (monitor,
1094 contact_monitor_presence_message_changed_cb, obj);
1098 contact_list_store_iface_setup (gpointer user_data)
1100 EmpathyContactListStore *store = user_data;
1101 EmpathyContactListStorePriv *priv = GET_PRIV (store);
1102 EmpathyContactMonitor *monitor;
1103 GList *contacts, *l;
1105 monitor = empathy_contact_list_get_monitor (priv->list);
1107 /* Signal connection. */
1108 g_signal_connect (monitor, "contact-added",
1109 G_CALLBACK (contact_monitor_contact_added_cb), store);
1110 g_signal_connect (monitor, "contact-removed",
1111 G_CALLBACK (contact_monitor_contact_removed_cb), store);
1112 g_signal_connect (monitor, "contact-presence-changed",
1113 G_CALLBACK (contact_monitor_presence_changed_cb), store);
1114 g_signal_connect (monitor, "contact-presence-message-changed",
1115 G_CALLBACK (contact_monitor_presence_message_changed_cb), store);
1116 g_signal_connect (monitor, "contact-name-changed",
1117 G_CALLBACK (contact_monitor_name_changed_cb), store);
1118 g_signal_connect (monitor, "contact-capabilities-changed",
1119 G_CALLBACK (contact_monitor_capabilities_changed_cb), store);
1120 g_signal_connect (monitor, "contact-avatar-changed",
1121 G_CALLBACK (contact_monitor_avatar_changed_cb), store);
1123 /* Add contacts already created. */
1124 contacts = empathy_contact_list_get_members (priv->list);
1125 for (l = contacts; l; l = l->next) {
1126 contact_monitor_contact_added_cb (monitor, l->data, store);
1128 g_object_unref (l->data);
1130 g_list_free (contacts);
1136 contact_list_store_contact_set_active (EmpathyContactListStore *store,
1137 EmpathyContact *contact,
1139 gboolean set_changed)
1141 EmpathyContactListStorePriv *priv;
1142 GtkTreeModel *model;
1145 priv = GET_PRIV (store);
1146 model = GTK_TREE_MODEL (store);
1148 iters = contact_list_store_find_contact (store, contact);
1149 for (l = iters; l; l = l->next) {
1152 gtk_tree_store_set (GTK_TREE_STORE (store), l->data,
1153 EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, active,
1156 DEBUG ("Set item %s", active ? "active" : "inactive");
1159 path = gtk_tree_model_get_path (model, l->data);
1160 gtk_tree_model_row_changed (model, path, l->data);
1161 gtk_tree_path_free (path);
1165 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
1166 g_list_free (iters);
1170 static ShowActiveData *
1171 contact_list_store_contact_active_new (EmpathyContactListStore *store,
1172 EmpathyContact *contact,
1175 ShowActiveData *data;
1177 DEBUG ("Contact:'%s' now active, and %s be removed",
1178 empathy_contact_get_name (contact),
1179 remove ? "WILL" : "WILL NOT");
1181 data = g_slice_new0 (ShowActiveData);
1183 data->store = g_object_ref (store);
1184 data->contact = g_object_ref (contact);
1185 data->remove = remove;
1191 contact_list_store_contact_active_free (ShowActiveData *data)
1193 g_object_unref (data->contact);
1194 g_object_unref (data->store);
1196 g_slice_free (ShowActiveData, data);
1200 contact_list_store_contact_active_cb (ShowActiveData *data)
1202 EmpathyContactListStorePriv *priv;
1204 priv = GET_PRIV (data->store);
1207 !priv->show_offline &&
1208 !empathy_contact_is_online (data->contact)) {
1209 DEBUG ("Contact:'%s' active timeout, removing item",
1210 empathy_contact_get_name (data->contact));
1211 contact_list_store_remove_contact (data->store, data->contact);
1214 DEBUG ("Contact:'%s' no longer active",
1215 empathy_contact_get_name (data->contact));
1217 contact_list_store_contact_set_active (data->store,
1222 contact_list_store_contact_active_free (data);
1228 contact_list_store_get_group_foreach (GtkTreeModel *model,
1236 /* Groups are only at the top level. */
1237 if (gtk_tree_path_get_depth (path) != 1) {
1241 gtk_tree_model_get (model, iter,
1242 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &str,
1243 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, &is_group,
1246 if (is_group && !tp_strdiff (str, fg->name)) {
1257 contact_list_store_get_group (EmpathyContactListStore *store,
1259 GtkTreeIter *iter_group_to_set,
1260 GtkTreeIter *iter_separator_to_set,
1263 EmpathyContactListStorePriv *priv;
1264 GtkTreeModel *model;
1265 GtkTreeIter iter_group;
1266 GtkTreeIter iter_separator;
1269 priv = GET_PRIV (store);
1271 memset (&fg, 0, sizeof (fg));
1275 model = GTK_TREE_MODEL (store);
1276 gtk_tree_model_foreach (model,
1277 (GtkTreeModelForeachFunc) contact_list_store_get_group_foreach,
1285 gtk_tree_store_append (GTK_TREE_STORE (store), &iter_group, NULL);
1286 gtk_tree_store_set (GTK_TREE_STORE (store), &iter_group,
1287 EMPATHY_CONTACT_LIST_STORE_COL_ICON_STATUS, NULL,
1288 EMPATHY_CONTACT_LIST_STORE_COL_NAME, name,
1289 EMPATHY_CONTACT_LIST_STORE_COL_IS_GROUP, TRUE,
1290 EMPATHY_CONTACT_LIST_STORE_COL_IS_ACTIVE, FALSE,
1291 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, FALSE,
1294 if (iter_group_to_set) {
1295 *iter_group_to_set = iter_group;
1298 gtk_tree_store_append (GTK_TREE_STORE (store),
1301 gtk_tree_store_set (GTK_TREE_STORE (store), &iter_separator,
1302 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, TRUE,
1305 if (iter_separator_to_set) {
1306 *iter_separator_to_set = iter_separator;
1313 if (iter_group_to_set) {
1314 *iter_group_to_set = fg.iter;
1317 iter_separator = fg.iter;
1319 if (gtk_tree_model_iter_next (model, &iter_separator)) {
1320 gboolean is_separator;
1322 gtk_tree_model_get (model, &iter_separator,
1323 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator,
1326 if (is_separator && iter_separator_to_set) {
1327 *iter_separator_to_set = iter_separator;
1334 contact_list_store_ordered_presence (McPresence state)
1337 case MC_PRESENCE_UNSET:
1338 case MC_PRESENCE_OFFLINE:
1340 case MC_PRESENCE_AVAILABLE:
1342 case MC_PRESENCE_AWAY:
1344 case MC_PRESENCE_EXTENDED_AWAY:
1346 case MC_PRESENCE_HIDDEN:
1348 case MC_PRESENCE_DO_NOT_DISTURB:
1351 g_return_val_if_reached (6);
1356 contact_list_store_state_sort_func (GtkTreeModel *model,
1357 GtkTreeIter *iter_a,
1358 GtkTreeIter *iter_b,
1362 gchar *name_a, *name_b;
1363 gboolean is_separator_a, is_separator_b;
1364 EmpathyContact *contact_a, *contact_b;
1365 guint presence_a, presence_b;
1367 gtk_tree_model_get (model, iter_a,
1368 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_a,
1369 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_a,
1370 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_a,
1372 gtk_tree_model_get (model, iter_b,
1373 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_b,
1374 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_b,
1375 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_b,
1378 /* Separator or group? */
1379 if (is_separator_a || is_separator_b) {
1380 if (is_separator_a) {
1382 } else if (is_separator_b) {
1385 } else if (!contact_a && contact_b) {
1387 } else if (contact_a && !contact_b) {
1389 } else if (!contact_a && !contact_b) {
1391 ret_val = g_utf8_collate (name_a, name_b);
1398 /* If we managed to get this far, we can start looking at
1401 presence_a = empathy_contact_get_presence (EMPATHY_CONTACT (contact_a));
1402 presence_a = contact_list_store_ordered_presence (presence_a);
1403 presence_b = empathy_contact_get_presence (EMPATHY_CONTACT (contact_b));
1404 presence_b = contact_list_store_ordered_presence (presence_b);
1406 if (presence_a < presence_b) {
1408 } else if (presence_a > presence_b) {
1411 /* Fallback: compare by name */
1412 ret_val = g_utf8_collate (name_a, name_b);
1420 g_object_unref (contact_a);
1424 g_object_unref (contact_b);
1431 contact_list_store_name_sort_func (GtkTreeModel *model,
1432 GtkTreeIter *iter_a,
1433 GtkTreeIter *iter_b,
1436 gchar *name_a, *name_b;
1437 EmpathyContact *contact_a, *contact_b;
1438 gboolean is_separator_a, is_separator_b;
1441 gtk_tree_model_get (model, iter_a,
1442 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_a,
1443 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_a,
1444 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_a,
1446 gtk_tree_model_get (model, iter_b,
1447 EMPATHY_CONTACT_LIST_STORE_COL_NAME, &name_b,
1448 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact_b,
1449 EMPATHY_CONTACT_LIST_STORE_COL_IS_SEPARATOR, &is_separator_b,
1452 /* If contact is NULL it means it's a group. */
1454 if (is_separator_a || is_separator_b) {
1455 if (is_separator_a) {
1457 } else if (is_separator_b) {
1460 } else if (!contact_a && contact_b) {
1462 } else if (contact_a && !contact_b) {
1465 ret_val = g_utf8_collate (name_a, name_b);
1472 g_object_unref (contact_a);
1476 g_object_unref (contact_b);
1483 contact_list_store_find_contact_foreach (GtkTreeModel *model,
1488 EmpathyContact *contact;
1490 gtk_tree_model_get (model, iter,
1491 EMPATHY_CONTACT_LIST_STORE_COL_CONTACT, &contact,
1494 if (contact == fc->contact) {
1496 fc->iters = g_list_append (fc->iters, gtk_tree_iter_copy (iter));
1500 g_object_unref (contact);
1507 contact_list_store_find_contact (EmpathyContactListStore *store,
1508 EmpathyContact *contact)
1510 EmpathyContactListStorePriv *priv;
1511 GtkTreeModel *model;
1515 priv = GET_PRIV (store);
1517 memset (&fc, 0, sizeof (fc));
1519 fc.contact = contact;
1521 model = GTK_TREE_MODEL (store);
1522 gtk_tree_model_foreach (model,
1523 (GtkTreeModelForeachFunc) contact_list_store_find_contact_foreach,
1534 contact_list_store_update_list_mode_foreach (GtkTreeModel *model,
1537 EmpathyContactListStore *store)
1539 EmpathyContactListStorePriv *priv;
1540 gboolean show_avatar = FALSE;
1542 priv = GET_PRIV (store);
1544 if (priv->show_avatars && !priv->is_compact) {
1548 gtk_tree_store_set (GTK_TREE_STORE (store), iter,
1549 EMPATHY_CONTACT_LIST_STORE_COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
1550 EMPATHY_CONTACT_LIST_STORE_COL_STATUS_VISIBLE, !priv->is_compact,