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-2010 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., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301 USA
21 * Authors: Mikael Hallendal <micke@imendio.com>
22 * Martyn Russell <martyn@imendio.com>
23 * Xavier Claessens <xclaesse@gmail.com>
24 * Travis Reitter <travis.reitter@collabora.co.uk>
32 #include <glib/gi18n-lib.h>
35 #include <folks/folks.h>
36 #include <folks/folks-telepathy.h>
37 #include <telepathy-glib/telepathy-glib.h>
39 #include <libempathy/empathy-utils.h>
40 #include <libempathy/empathy-enum-types.h>
42 #include "empathy-individual-store.h"
43 #include "empathy-ui-utils.h"
44 #include "empathy-gtk-enum-types.h"
46 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
47 #include <libempathy/empathy-debug.h>
49 /* Active users are those which have recently changed state
50 * (e.g. online, offline or from normal to a busy state).
53 /* Time in seconds user is shown as active */
54 #define ACTIVE_USER_SHOW_TIME 7
56 /* Time in seconds after connecting which we wait before active users are enabled */
57 #define ACTIVE_USER_WAIT_TO_ENABLE_TIME 5
59 struct _EmpathyIndividualStorePriv
61 gboolean show_avatars;
64 gboolean show_protocols;
65 EmpathyIndividualStoreSort sort_criterium;
67 gboolean dispose_has_run;
68 GHashTable *status_icons;
69 /* List of owned GCancellables for each pending avatar load operation */
70 GList *avatar_cancellables;
71 /* Hash: FolksIndividual* -> GQueue (GtkTreeIter *) */
72 GHashTable *folks_individual_cache;
73 /* Hash: char *groupname -> GtkTreeIter * */
74 GHashTable *empathy_group_cache;
80 EmpathyIndividualStore *self;
81 FolksIndividual *individual;
96 /* prototypes to break cycles */
97 static void individual_store_contact_update (EmpathyIndividualStore *self,
98 FolksIndividual *individual);
100 G_DEFINE_TYPE (EmpathyIndividualStore, empathy_individual_store,
101 GTK_TYPE_TREE_STORE);
104 add_individual_to_store (GtkTreeStore *store,
107 FolksIndividual *individual)
109 EmpathyIndividualStore *self = EMPATHY_INDIVIDUAL_STORE (store);
110 gboolean can_audio_call, can_video_call;
111 const gchar * const *types;
114 empathy_individual_can_audio_video_call (individual, &can_audio_call,
115 &can_video_call, NULL);
117 types = empathy_individual_get_client_types (individual);
119 gtk_tree_store_insert_with_values (store, iter, parent, 0,
120 EMPATHY_INDIVIDUAL_STORE_COL_NAME,
121 folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual)),
122 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, individual,
123 EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, FALSE,
124 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, FALSE,
125 EMPATHY_INDIVIDUAL_STORE_COL_CAN_AUDIO_CALL, can_audio_call,
126 EMPATHY_INDIVIDUAL_STORE_COL_CAN_VIDEO_CALL, can_video_call,
127 EMPATHY_INDIVIDUAL_STORE_COL_CLIENT_TYPES, types,
130 queue = g_hash_table_lookup (self->priv->folks_individual_cache, individual);
133 g_queue_push_tail (queue, gtk_tree_iter_copy (iter));
137 queue = g_queue_new ();
138 g_queue_push_tail (queue, gtk_tree_iter_copy (iter));
139 g_hash_table_insert (self->priv->folks_individual_cache, individual,
145 individual_store_get_group (EmpathyIndividualStore *self,
147 GtkTreeIter *iter_group_to_set,
148 GtkTreeIter *iter_separator_to_set,
150 gboolean is_fake_group)
153 GtkTreeIter iter_group;
154 GtkTreeIter iter_separator;
157 model = GTK_TREE_MODEL (self);
158 iter = g_hash_table_lookup (self->priv->empathy_group_cache, name);
165 gtk_tree_store_insert_with_values (GTK_TREE_STORE (self), &iter_group,
167 EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS, NULL,
168 EMPATHY_INDIVIDUAL_STORE_COL_NAME, name,
169 EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, TRUE,
170 EMPATHY_INDIVIDUAL_STORE_COL_IS_ACTIVE, FALSE,
171 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, FALSE,
172 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, is_fake_group,
175 g_hash_table_insert (self->priv->empathy_group_cache, g_strdup (name),
176 gtk_tree_iter_copy (&iter_group));
178 if (iter_group_to_set)
179 *iter_group_to_set = iter_group;
181 gtk_tree_store_insert_with_values (GTK_TREE_STORE (self), &iter_separator,
183 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, TRUE,
186 if (iter_separator_to_set)
187 *iter_separator_to_set = iter_separator;
194 if (iter_group_to_set)
195 *iter_group_to_set = *iter;
197 iter_separator = *iter;
199 if (gtk_tree_model_iter_next (model, &iter_separator))
201 gboolean is_separator;
203 gtk_tree_model_get (model, &iter_separator,
204 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator, -1);
206 if (is_separator && iter_separator_to_set)
207 *iter_separator_to_set = iter_separator;
213 individual_store_find_contact (EmpathyIndividualStore *self,
214 FolksIndividual *individual)
216 GQueue *row_refs_queue;
218 GList *iters_list = NULL;
220 row_refs_queue = g_hash_table_lookup (self->priv->folks_individual_cache,
225 for (i = g_queue_peek_head_link (row_refs_queue) ; i != NULL ; i = i->next)
227 GtkTreeIter *iter = i->data;
229 iters_list = g_list_prepend (iters_list, gtk_tree_iter_copy (iter));
236 free_iters (GList *iters)
238 g_list_foreach (iters, (GFunc) gtk_tree_iter_free, NULL);
243 empathy_individual_store_remove_individual (EmpathyIndividualStore *self,
244 FolksIndividual *individual)
250 row_refs = g_hash_table_lookup (self->priv->folks_individual_cache,
256 model = GTK_TREE_MODEL (self);
258 for (l = g_queue_peek_head_link (row_refs); l; l = l->next)
260 GtkTreeIter *iter = l->data;
263 /* NOTE: it is only <= 2 here because we have
264 * separators after the group name, otherwise it
267 if (gtk_tree_model_iter_parent (model, &parent, iter) &&
268 gtk_tree_model_iter_n_children (model, &parent) <= 2)
271 gtk_tree_model_get (model, &parent,
272 EMPATHY_INDIVIDUAL_STORE_COL_NAME, &group_name,
274 g_hash_table_remove (self->priv->empathy_group_cache,
276 gtk_tree_store_remove (GTK_TREE_STORE (self), &parent);
280 gtk_tree_store_remove (GTK_TREE_STORE (self), iter);
284 g_hash_table_remove (self->priv->folks_individual_cache, individual);
288 empathy_individual_store_add_individual (EmpathyIndividualStore *self,
289 FolksIndividual *individual)
291 GtkTreeIter iter, iter_group;
292 GeeSet *group_set = NULL;
293 gboolean grouped = FALSE;
295 if (EMP_STR_EMPTY (folks_alias_details_get_alias (
296 FOLKS_ALIAS_DETAILS (individual))))
299 if (!self->priv->show_groups)
301 /* add our individual to the toplevel of the store */
302 add_individual_to_store (GTK_TREE_STORE (self), &iter, NULL,
308 group_set = folks_group_details_get_groups (
309 FOLKS_GROUP_DETAILS (individual));
311 if (gee_collection_get_size (GEE_COLLECTION (group_set)) > 0)
313 /* add the contact to its groups */
314 GeeIterator *group_iter =
315 gee_iterable_iterator (GEE_ITERABLE (group_set));
317 while (group_iter != NULL && gee_iterator_next (group_iter))
319 gchar *group_name = gee_iterator_get (group_iter);
321 individual_store_get_group (self, group_name, &iter_group,
324 add_individual_to_store (GTK_TREE_STORE (self), &iter, &iter_group,
331 g_clear_object (&group_iter);
335 /* fall-back groups, in case there are no named groups */
336 EmpathyContact *contact;
337 TpConnection *connection;
338 const gchar *protocol_name = NULL;
340 contact = empathy_contact_dup_from_folks_individual (individual);
343 connection = empathy_contact_get_connection (contact);
344 protocol_name = tp_connection_get_protocol_name (connection);
347 if (!tp_strdiff (protocol_name, "local-xmpp"))
349 /* these are People Nearby */
350 individual_store_get_group (self,
351 EMPATHY_INDIVIDUAL_STORE_PEOPLE_NEARBY, &iter_group, NULL, NULL,
353 add_individual_to_store (GTK_TREE_STORE (self), &iter, &iter_group,
358 g_clear_object (&contact);
361 if (folks_favourite_details_get_is_favourite (
362 FOLKS_FAVOURITE_DETAILS (individual)))
364 /* Add contact to the fake 'Favorites' group */
365 individual_store_get_group (self, EMPATHY_INDIVIDUAL_STORE_FAVORITE,
366 &iter_group, NULL, NULL, TRUE);
368 add_individual_to_store (GTK_TREE_STORE (self), &iter, &iter_group,
375 /* Else add the contact to 'Ungrouped' */
376 individual_store_get_group (self,
377 EMPATHY_INDIVIDUAL_STORE_UNGROUPED,
378 &iter_group, NULL, NULL, TRUE);
379 add_individual_to_store (GTK_TREE_STORE (self), &iter, &iter_group,
385 individual_store_contact_update (self, individual);
389 individual_store_contact_set_active (EmpathyIndividualStore *self,
390 FolksIndividual *individual,
392 gboolean set_changed)
397 model = GTK_TREE_MODEL (self);
399 iters = individual_store_find_contact (self, individual);
400 for (l = iters; l; l = l->next)
404 gtk_tree_store_set (GTK_TREE_STORE (self), l->data,
405 EMPATHY_INDIVIDUAL_STORE_COL_IS_ACTIVE, active,
410 path = gtk_tree_model_get_path (model, l->data);
411 gtk_tree_model_row_changed (model, path, l->data);
412 gtk_tree_path_free (path);
419 static void individual_store_contact_active_free (ShowActiveData *data);
422 individual_store_contact_active_invalidated (ShowActiveData *data,
425 /* Remove the timeout and free the struct, since the individual or individual
426 * store has disappeared. */
427 g_source_remove (data->timeout);
429 if (old_object == (GObject *) data->self)
431 else if (old_object == (GObject *) data->individual)
432 data->individual = NULL;
434 g_assert_not_reached ();
436 individual_store_contact_active_free (data);
439 static ShowActiveData *
440 individual_store_contact_active_new (EmpathyIndividualStore *self,
441 FolksIndividual *individual,
444 ShowActiveData *data;
446 data = g_slice_new0 (ShowActiveData);
448 /* We don't actually want to force either the IndividualStore or the
449 * Individual to stay alive, since the user could quit Empathy or disable
450 * the account before the contact_active timeout is fired. */
451 g_object_weak_ref (G_OBJECT (self),
452 (GWeakNotify) individual_store_contact_active_invalidated, data);
453 g_object_weak_ref (G_OBJECT (individual),
454 (GWeakNotify) individual_store_contact_active_invalidated, data);
457 data->individual = individual;
458 data->remove = remove_;
465 individual_store_contact_active_free (ShowActiveData *data)
467 if (data->self != NULL)
469 g_object_weak_unref (G_OBJECT (data->self),
470 (GWeakNotify) individual_store_contact_active_invalidated, data);
473 if (data->individual != NULL)
475 g_object_weak_unref (G_OBJECT (data->individual),
476 (GWeakNotify) individual_store_contact_active_invalidated, data);
479 g_slice_free (ShowActiveData, data);
483 individual_store_contact_active_cb (ShowActiveData *data)
487 DEBUG ("Individual'%s' active timeout, removing item",
488 folks_alias_details_get_alias (
489 FOLKS_ALIAS_DETAILS (data->individual)));
490 empathy_individual_store_remove_individual (data->self, data->individual);
493 individual_store_contact_set_active (data->self,
494 data->individual, FALSE, TRUE);
496 individual_store_contact_active_free (data);
502 EmpathyIndividualStore *store; /* weak */
503 GCancellable *cancellable; /* owned */
507 individual_avatar_pixbuf_received_cb (FolksIndividual *individual,
508 GAsyncResult *result,
509 LoadAvatarData *data)
511 GError *error = NULL;
514 pixbuf = empathy_pixbuf_avatar_from_individual_scaled_finish (individual,
519 /* No need to display an error if the individal just doesn't have an
521 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
523 DEBUG ("failed to retrieve pixbuf for individual %s: %s",
524 folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual)),
528 g_clear_error (&error);
530 else if (data->store != NULL)
534 iters = individual_store_find_contact (data->store, individual);
535 for (l = iters; l; l = l->next)
537 gtk_tree_store_set (GTK_TREE_STORE (data->store), l->data,
538 EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR, pixbuf,
546 if (data->store != NULL)
548 g_object_remove_weak_pointer (G_OBJECT (data->store),
549 (gpointer *) &data->store);
550 data->store->priv->avatar_cancellables = g_list_remove (
551 data->store->priv->avatar_cancellables, data->cancellable);
554 tp_clear_object (&pixbuf);
555 g_object_unref (data->cancellable);
556 g_slice_free (LoadAvatarData, data);
560 individual_store_contact_update (EmpathyIndividualStore *self,
561 FolksIndividual *individual)
563 ShowActiveData *data;
567 gboolean was_online = TRUE;
568 gboolean now_online = FALSE;
569 gboolean set_model = FALSE;
570 gboolean do_remove = FALSE;
571 gboolean do_set_active = FALSE;
572 gboolean do_set_refresh = FALSE;
573 gboolean show_avatar = FALSE;
574 GdkPixbuf *pixbuf_status;
575 LoadAvatarData *load_avatar_data;
577 model = GTK_TREE_MODEL (self);
579 iters = individual_store_find_contact (self, individual);
589 /* Get online state now. */
590 now_online = folks_presence_details_is_online (
591 FOLKS_PRESENCE_DETAILS (individual));
595 DEBUG ("Individual'%s' in list:NO, should be:YES",
596 folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual)));
598 empathy_individual_store_add_individual (self, individual);
600 if (self->priv->show_active)
602 do_set_active = TRUE;
607 /* Get online state before. */
608 if (iters && g_list_length (iters) > 0)
610 gtk_tree_model_get (model, iters->data,
611 EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, &was_online, -1);
614 /* Is this really an update or an online/offline. */
615 if (self->priv->show_active)
617 if (was_online != now_online)
619 do_set_active = TRUE;
620 do_set_refresh = TRUE;
624 /* Was TRUE for presence updates. */
625 /* do_set_active = FALSE; */
626 do_set_refresh = TRUE;
633 if (self->priv->show_avatars && !self->priv->is_compact)
638 /* Load the avatar asynchronously */
639 load_avatar_data = g_slice_new (LoadAvatarData);
640 load_avatar_data->store = self;
641 g_object_add_weak_pointer (G_OBJECT (self),
642 (gpointer *) &load_avatar_data->store);
643 load_avatar_data->cancellable = g_cancellable_new ();
645 self->priv->avatar_cancellables = g_list_prepend (
646 self->priv->avatar_cancellables, load_avatar_data->cancellable);
648 empathy_pixbuf_avatar_from_individual_scaled_async (individual, 32, 32,
649 load_avatar_data->cancellable,
650 (GAsyncReadyCallback) individual_avatar_pixbuf_received_cb,
654 empathy_individual_store_get_individual_status_icon (self, individual);
656 for (l = iters; l && set_model; l = l->next)
658 gboolean can_audio_call, can_video_call;
659 const gchar * const *types;
661 empathy_individual_can_audio_video_call (individual, &can_audio_call,
662 &can_video_call, NULL);
664 types = empathy_individual_get_client_types (individual);
666 gtk_tree_store_set (GTK_TREE_STORE (self), l->data,
667 EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS, pixbuf_status,
668 EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
669 EMPATHY_INDIVIDUAL_STORE_COL_NAME,
670 folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual)),
671 EMPATHY_INDIVIDUAL_STORE_COL_PRESENCE_TYPE,
672 folks_presence_details_get_presence_type (
673 FOLKS_PRESENCE_DETAILS (individual)),
674 EMPATHY_INDIVIDUAL_STORE_COL_STATUS,
675 folks_presence_details_get_presence_message (
676 FOLKS_PRESENCE_DETAILS (individual)),
677 EMPATHY_INDIVIDUAL_STORE_COL_COMPACT, self->priv->is_compact,
678 EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, FALSE,
679 EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE, now_online,
680 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, FALSE,
681 EMPATHY_INDIVIDUAL_STORE_COL_CAN_AUDIO_CALL, can_audio_call,
682 EMPATHY_INDIVIDUAL_STORE_COL_CAN_VIDEO_CALL, can_video_call,
683 EMPATHY_INDIVIDUAL_STORE_COL_CLIENT_TYPES, types,
687 if (self->priv->show_active && do_set_active)
689 individual_store_contact_set_active (self, individual, do_set_active,
695 individual_store_contact_active_new (self, individual,
697 data->timeout = g_timeout_add_seconds (ACTIVE_USER_SHOW_TIME,
698 (GSourceFunc) individual_store_contact_active_cb, data);
702 /* FIXME: when someone goes online then offline quickly, the
703 * first timeout sets the user to be inactive and the second
704 * timeout removes the user from the contact list, really we
705 * should remove the first timeout.
711 individual_store_individual_updated_cb (FolksIndividual *individual,
713 EmpathyIndividualStore *self)
715 individual_store_contact_update (self, individual);
719 individual_store_contact_updated_cb (EmpathyContact *contact,
721 EmpathyIndividualStore *self)
723 FolksIndividual *individual;
725 individual = g_object_get_data (G_OBJECT (contact), "individual");
726 if (individual == NULL)
729 individual_store_contact_update (self, individual);
733 individual_personas_changed_cb (FolksIndividual *individual,
736 EmpathyIndividualStore *self)
740 iter = gee_iterable_iterator (GEE_ITERABLE (removed));
741 /* FIXME: libfolks hasn't grown capabilities support yet, so we have to go
742 * through the EmpathyContacts for them. */
743 while (gee_iterator_next (iter))
745 TpfPersona *persona = gee_iterator_get (iter);
746 TpContact *tp_contact;
747 EmpathyContact *contact;
749 if (TPF_IS_PERSONA (persona))
751 tp_contact = tpf_persona_get_contact (persona);
752 if (tp_contact != NULL)
754 contact = empathy_contact_dup_from_tp_contact (tp_contact);
755 empathy_contact_set_persona (contact, FOLKS_PERSONA (persona));
757 g_object_set_data (G_OBJECT (contact), "individual", NULL);
758 g_signal_handlers_disconnect_by_func (contact,
759 (GCallback) individual_store_contact_updated_cb, self);
761 g_object_unref (contact);
765 g_clear_object (&persona);
767 g_clear_object (&iter);
769 iter = gee_iterable_iterator (GEE_ITERABLE (added));
770 while (gee_iterator_next (iter))
772 TpfPersona *persona = gee_iterator_get (iter);
773 TpContact *tp_contact;
774 EmpathyContact *contact;
776 if (TPF_IS_PERSONA (persona))
778 tp_contact = tpf_persona_get_contact (persona);
779 if (tp_contact != NULL)
781 contact = empathy_contact_dup_from_tp_contact (tp_contact);
782 empathy_contact_set_persona (contact, FOLKS_PERSONA (persona));
784 g_object_set_data (G_OBJECT (contact), "individual", individual);
785 g_signal_connect (contact, "notify::capabilities",
786 (GCallback) individual_store_contact_updated_cb, self);
787 g_signal_connect (contact, "notify::client-types",
788 (GCallback) individual_store_contact_updated_cb, self);
790 g_object_unref (contact);
794 g_clear_object (&persona);
796 g_clear_object (&iter);
800 individual_store_favourites_changed_cb (FolksIndividual *individual,
802 EmpathyIndividualStore *self)
804 DEBUG ("Individual %s is %s a favourite",
805 folks_individual_get_id (individual),
806 folks_favourite_details_get_is_favourite (
807 FOLKS_FAVOURITE_DETAILS (individual)) ? "now" : "no longer");
809 empathy_individual_store_remove_individual (self, individual);
810 empathy_individual_store_add_individual (self, individual);
814 individual_store_add_individual_and_connect (EmpathyIndividualStore *self,
815 FolksIndividual *individual)
817 GeeSet *empty_set = gee_set_empty (G_TYPE_NONE, NULL, NULL);
819 empathy_individual_store_add_individual (self, individual);
821 g_signal_connect (individual, "notify::avatar",
822 (GCallback) individual_store_individual_updated_cb, self);
823 g_signal_connect (individual, "notify::presence-type",
824 (GCallback) individual_store_individual_updated_cb, self);
825 g_signal_connect (individual, "notify::presence-message",
826 (GCallback) individual_store_individual_updated_cb, self);
827 g_signal_connect (individual, "notify::alias",
828 (GCallback) individual_store_individual_updated_cb, self);
829 g_signal_connect (individual, "personas-changed",
830 (GCallback) individual_personas_changed_cb, self);
831 g_signal_connect (individual, "notify::is-favourite",
832 (GCallback) individual_store_favourites_changed_cb, self);
834 /* provide an empty set so the callback can assume non-NULL sets */
835 individual_personas_changed_cb (individual,
836 folks_individual_get_personas (individual), empty_set, self);
837 g_clear_object (&empty_set);
841 empathy_individual_store_disconnect_individual (EmpathyIndividualStore *self,
842 FolksIndividual *individual)
844 GeeSet *empty_set = gee_set_empty (G_TYPE_NONE, NULL, NULL);
846 /* provide an empty set so the callback can assume non-NULL sets */
847 individual_personas_changed_cb (individual, empty_set,
848 folks_individual_get_personas (individual), self);
849 g_clear_object (&empty_set);
851 g_signal_handlers_disconnect_by_func (individual,
852 (GCallback) individual_store_individual_updated_cb, self);
853 g_signal_handlers_disconnect_by_func (individual,
854 (GCallback) individual_personas_changed_cb, self);
855 g_signal_handlers_disconnect_by_func (individual,
856 (GCallback) individual_store_favourites_changed_cb, self);
860 individual_store_remove_individual_and_disconnect (
861 EmpathyIndividualStore *self,
862 FolksIndividual *individual)
864 empathy_individual_store_disconnect_individual (self, individual);
865 empathy_individual_store_remove_individual (self, individual);
869 individual_store_dispose (GObject *object)
871 EmpathyIndividualStore *self = EMPATHY_INDIVIDUAL_STORE (object);
874 if (self->priv->dispose_has_run)
876 self->priv->dispose_has_run = TRUE;
878 /* Cancel any pending avatar load operations */
879 for (l = self->priv->avatar_cancellables; l != NULL; l = l->next)
881 /* The cancellables are freed in individual_avatar_pixbuf_received_cb() */
882 g_cancellable_cancel (G_CANCELLABLE (l->data));
884 g_list_free (self->priv->avatar_cancellables);
886 if (self->priv->inhibit_active)
888 g_source_remove (self->priv->inhibit_active);
891 g_hash_table_unref (self->priv->status_icons);
892 g_hash_table_unref (self->priv->folks_individual_cache);
893 g_hash_table_unref (self->priv->empathy_group_cache);
894 G_OBJECT_CLASS (empathy_individual_store_parent_class)->dispose (object);
898 individual_store_get_property (GObject *object,
903 EmpathyIndividualStore *self = EMPATHY_INDIVIDUAL_STORE (object);
907 case PROP_SHOW_AVATARS:
908 g_value_set_boolean (value, self->priv->show_avatars);
910 case PROP_SHOW_PROTOCOLS:
911 g_value_set_boolean (value, self->priv->show_protocols);
913 case PROP_SHOW_GROUPS:
914 g_value_set_boolean (value, self->priv->show_groups);
916 case PROP_IS_COMPACT:
917 g_value_set_boolean (value, self->priv->is_compact);
919 case PROP_SORT_CRITERIUM:
920 g_value_set_enum (value, self->priv->sort_criterium);
923 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
929 individual_store_set_property (GObject *object,
936 case PROP_SHOW_AVATARS:
937 empathy_individual_store_set_show_avatars (EMPATHY_INDIVIDUAL_STORE
938 (object), g_value_get_boolean (value));
940 case PROP_SHOW_PROTOCOLS:
941 empathy_individual_store_set_show_protocols (EMPATHY_INDIVIDUAL_STORE
942 (object), g_value_get_boolean (value));
944 case PROP_SHOW_GROUPS:
945 empathy_individual_store_set_show_groups (EMPATHY_INDIVIDUAL_STORE
946 (object), g_value_get_boolean (value));
948 case PROP_IS_COMPACT:
949 empathy_individual_store_set_is_compact (EMPATHY_INDIVIDUAL_STORE
950 (object), g_value_get_boolean (value));
952 case PROP_SORT_CRITERIUM:
953 empathy_individual_store_set_sort_criterium (EMPATHY_INDIVIDUAL_STORE
954 (object), g_value_get_enum (value));
957 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
963 empathy_individual_store_class_init (EmpathyIndividualStoreClass *klass)
965 GObjectClass *object_class = G_OBJECT_CLASS (klass);
967 object_class->dispose = individual_store_dispose;
968 object_class->get_property = individual_store_get_property;
969 object_class->set_property = individual_store_set_property;
971 g_object_class_install_property (object_class,
973 g_param_spec_boolean ("show-avatars",
975 "Whether contact list should display "
976 "avatars for contacts", TRUE, G_PARAM_READWRITE));
977 g_object_class_install_property (object_class,
979 g_param_spec_boolean ("show-protocols",
981 "Whether contact list should display "
982 "protocols for contacts", FALSE, G_PARAM_READWRITE));
983 g_object_class_install_property (object_class,
985 g_param_spec_boolean ("show-groups",
987 "Whether contact list should display "
988 "contact groups", TRUE, G_PARAM_READWRITE));
989 g_object_class_install_property (object_class,
991 g_param_spec_boolean ("is-compact",
993 "Whether the contact list is in compact mode or not",
994 FALSE, G_PARAM_READWRITE));
996 g_object_class_install_property (object_class,
998 g_param_spec_enum ("sort-criterium",
1000 "The sort criterium to use for sorting the contact list",
1001 EMPATHY_TYPE_INDIVIDUAL_STORE_SORT,
1002 EMPATHY_INDIVIDUAL_STORE_SORT_NAME, G_PARAM_READWRITE));
1004 g_type_class_add_private (object_class,
1005 sizeof (EmpathyIndividualStorePriv));
1009 get_position (const char **strv,
1014 for (i = 0; strv[i] != NULL; i++)
1016 if (!tp_strdiff (strv[i], str))
1024 compare_separator_and_groups (gboolean is_separator_a,
1025 gboolean is_separator_b,
1026 const gchar *name_a,
1027 const gchar *name_b,
1028 FolksIndividual *individual_a,
1029 FolksIndividual *individual_b,
1030 gboolean fake_group_a,
1031 gboolean fake_group_b)
1033 /* these two lists are the sorted list of fake groups to include at the
1034 * top and bottom of the roster */
1035 const char *top_groups[] = {
1036 EMPATHY_INDIVIDUAL_STORE_FAVORITE,
1040 const char *bottom_groups[] = {
1041 EMPATHY_INDIVIDUAL_STORE_UNGROUPED,
1045 if (is_separator_a || is_separator_b)
1047 /* We have at least one separator */
1052 else if (is_separator_b)
1058 /* One group and one contact */
1059 if (!individual_a && individual_b)
1063 else if (individual_a && !individual_b)
1067 else if (!individual_a && !individual_b)
1069 gboolean a_in_top, b_in_top, a_in_bottom, b_in_bottom;
1071 a_in_top = fake_group_a && tp_strv_contains (top_groups, name_a);
1072 b_in_top = fake_group_b && tp_strv_contains (top_groups, name_b);
1073 a_in_bottom = fake_group_a && tp_strv_contains (bottom_groups, name_a);
1074 b_in_bottom = fake_group_b && tp_strv_contains (bottom_groups, name_b);
1076 if (a_in_top && b_in_top)
1078 /* compare positions */
1079 return CLAMP (get_position (top_groups, name_a) -
1080 get_position (top_groups, name_b), -1, 1);
1082 else if (a_in_bottom && b_in_bottom)
1084 /* compare positions */
1085 return CLAMP (get_position (bottom_groups, name_a) -
1086 get_position (bottom_groups, name_b), -1, 1);
1088 else if (a_in_top || b_in_bottom)
1092 else if (b_in_top || a_in_bottom)
1098 return g_utf8_collate (name_a, name_b);
1102 /* Two contacts, ordering depends of the sorting policy */
1107 individual_store_contact_sort (FolksIndividual *individual_a,
1108 FolksIndividual *individual_b)
1111 EmpathyContact *contact_a = NULL, *contact_b = NULL;
1112 TpAccount *account_a, *account_b;
1114 g_return_val_if_fail (individual_a != NULL || individual_b != NULL, 0);
1117 ret_val = g_utf8_collate (
1118 folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual_a)),
1119 folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (individual_b)));
1124 contact_a = empathy_contact_dup_from_folks_individual (individual_a);
1125 contact_b = empathy_contact_dup_from_folks_individual (individual_b);
1126 if (contact_a != NULL && contact_b != NULL)
1128 account_a = empathy_contact_get_account (contact_a);
1129 account_b = empathy_contact_get_account (contact_b);
1131 g_assert (account_a != NULL);
1132 g_assert (account_b != NULL);
1135 ret_val = g_strcmp0 (tp_account_get_protocol_name (account_a),
1136 tp_account_get_protocol_name (account_b));
1142 ret_val = g_strcmp0 (tp_proxy_get_object_path (account_a),
1143 tp_proxy_get_object_path (account_b));
1150 ret_val = g_utf8_collate (folks_individual_get_id (individual_a),
1151 folks_individual_get_id (individual_b));
1154 tp_clear_object (&contact_a);
1155 tp_clear_object (&contact_b);
1161 individual_store_state_sort_func (GtkTreeModel *model,
1162 GtkTreeIter *iter_a,
1163 GtkTreeIter *iter_b,
1167 FolksIndividual *individual_a, *individual_b;
1168 gchar *name_a, *name_b;
1169 gboolean is_separator_a, is_separator_b;
1170 gboolean fake_group_a, fake_group_b;
1171 FolksPresenceType folks_presence_type_a, folks_presence_type_b;
1172 TpConnectionPresenceType tp_presence_a, tp_presence_b;
1174 gtk_tree_model_get (model, iter_a,
1175 EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name_a,
1176 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual_a,
1177 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator_a,
1178 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, &fake_group_a, -1);
1179 gtk_tree_model_get (model, iter_b,
1180 EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name_b,
1181 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual_b,
1182 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator_b,
1183 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, &fake_group_b, -1);
1185 if (individual_a == NULL || individual_b == NULL)
1187 ret_val = compare_separator_and_groups (is_separator_a, is_separator_b,
1188 name_a, name_b, individual_a, individual_b, fake_group_a,
1193 /* If we managed to get this far, we can start looking at
1196 folks_presence_type_a =
1197 folks_presence_details_get_presence_type (
1198 FOLKS_PRESENCE_DETAILS (individual_a));
1199 folks_presence_type_b =
1200 folks_presence_details_get_presence_type (
1201 FOLKS_PRESENCE_DETAILS (individual_b));
1202 tp_presence_a = empathy_folks_presence_type_to_tp (folks_presence_type_a);
1203 tp_presence_b = empathy_folks_presence_type_to_tp (folks_presence_type_b);
1205 ret_val = -tp_connection_presence_type_cmp_availability (tp_presence_a,
1210 /* Fallback: compare by name et al. */
1211 ret_val = individual_store_contact_sort (individual_a, individual_b);
1217 tp_clear_object (&individual_a);
1218 tp_clear_object (&individual_b);
1224 individual_store_name_sort_func (GtkTreeModel *model,
1225 GtkTreeIter *iter_a,
1226 GtkTreeIter *iter_b,
1229 gchar *name_a, *name_b;
1230 FolksIndividual *individual_a, *individual_b;
1231 gboolean is_separator_a = FALSE, is_separator_b = FALSE;
1233 gboolean fake_group_a, fake_group_b;
1235 gtk_tree_model_get (model, iter_a,
1236 EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name_a,
1237 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual_a,
1238 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator_a,
1239 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, &fake_group_a, -1);
1240 gtk_tree_model_get (model, iter_b,
1241 EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name_b,
1242 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual_b,
1243 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator_b,
1244 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, &fake_group_b, -1);
1246 if (individual_a == NULL || individual_b == NULL)
1247 ret_val = compare_separator_and_groups (is_separator_a, is_separator_b,
1248 name_a, name_b, individual_a, individual_b, fake_group_a, fake_group_b);
1250 ret_val = individual_store_contact_sort (individual_a, individual_b);
1252 tp_clear_object (&individual_a);
1253 tp_clear_object (&individual_b);
1261 individual_store_setup (EmpathyIndividualStore *self)
1264 GDK_TYPE_PIXBUF, /* Status pixbuf */
1265 GDK_TYPE_PIXBUF, /* Avatar pixbuf */
1266 G_TYPE_BOOLEAN, /* Avatar pixbuf visible */
1267 G_TYPE_STRING, /* Name */
1268 G_TYPE_UINT, /* Presence type */
1269 G_TYPE_STRING, /* Status string */
1270 G_TYPE_BOOLEAN, /* Compact view */
1271 FOLKS_TYPE_INDIVIDUAL, /* Individual type */
1272 G_TYPE_BOOLEAN, /* Is group */
1273 G_TYPE_BOOLEAN, /* Is active */
1274 G_TYPE_BOOLEAN, /* Is online */
1275 G_TYPE_BOOLEAN, /* Is separator */
1276 G_TYPE_BOOLEAN, /* Can make audio calls */
1277 G_TYPE_BOOLEAN, /* Can make video calls */
1278 G_TYPE_BOOLEAN, /* Is a fake group */
1279 G_TYPE_STRV, /* Client types */
1280 G_TYPE_UINT, /* Event count */
1283 gtk_tree_store_set_column_types (GTK_TREE_STORE (self),
1284 EMPATHY_INDIVIDUAL_STORE_COL_COUNT, types);
1286 /* Set up sorting */
1287 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (self),
1288 EMPATHY_INDIVIDUAL_STORE_COL_NAME,
1289 individual_store_name_sort_func, self, NULL);
1290 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (self),
1291 EMPATHY_INDIVIDUAL_STORE_COL_STATUS,
1292 individual_store_state_sort_func, self, NULL);
1294 self->priv->sort_criterium = EMPATHY_INDIVIDUAL_STORE_SORT_NAME;
1296 empathy_individual_store_set_sort_criterium (self,
1297 self->priv->sort_criterium);
1301 individual_store_inhibit_active_cb (EmpathyIndividualStore *self)
1303 self->priv->show_active = TRUE;
1304 self->priv->inhibit_active = 0;
1310 g_queue_free_full_iter (gpointer data)
1312 GQueue *queue = (GQueue *) data;
1313 g_queue_foreach (queue, (GFunc) gtk_tree_iter_free, NULL);
1314 g_queue_free (queue);
1318 empathy_individual_store_init (EmpathyIndividualStore *self)
1320 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
1321 EMPATHY_TYPE_INDIVIDUAL_STORE, EmpathyIndividualStorePriv);
1323 self->priv->show_avatars = TRUE;
1324 self->priv->show_groups = TRUE;
1325 self->priv->show_protocols = FALSE;
1326 self->priv->inhibit_active =
1327 g_timeout_add_seconds (ACTIVE_USER_WAIT_TO_ENABLE_TIME,
1328 (GSourceFunc) individual_store_inhibit_active_cb, self);
1329 self->priv->status_icons =
1330 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
1331 self->priv->folks_individual_cache = g_hash_table_new_full (NULL, NULL, NULL,
1332 g_queue_free_full_iter);
1333 self->priv->empathy_group_cache = g_hash_table_new_full (g_str_hash,
1334 g_str_equal, g_free, (GDestroyNotify) gtk_tree_iter_free);
1335 individual_store_setup (self);
1339 empathy_individual_store_get_show_avatars (EmpathyIndividualStore *self)
1341 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self), TRUE);
1343 return self->priv->show_avatars;
1347 individual_store_update_list_mode_foreach (GtkTreeModel *model,
1350 EmpathyIndividualStore *self)
1352 gboolean show_avatar = FALSE;
1353 FolksIndividual *individual;
1354 GdkPixbuf *pixbuf_status;
1356 if (self->priv->show_avatars && !self->priv->is_compact)
1361 gtk_tree_model_get (model, iter,
1362 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL, &individual, -1);
1364 if (individual == NULL)
1368 /* get icon from hash_table */
1370 empathy_individual_store_get_individual_status_icon (self, individual);
1372 gtk_tree_store_set (GTK_TREE_STORE (self), iter,
1373 EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS, pixbuf_status,
1374 EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR_VISIBLE, show_avatar,
1375 EMPATHY_INDIVIDUAL_STORE_COL_COMPACT, self->priv->is_compact, -1);
1377 g_object_unref (individual);
1383 empathy_individual_store_set_show_avatars (EmpathyIndividualStore *self,
1384 gboolean show_avatars)
1386 GtkTreeModel *model;
1388 g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self));
1390 self->priv->show_avatars = show_avatars;
1392 model = GTK_TREE_MODEL (self);
1394 gtk_tree_model_foreach (model,
1395 (GtkTreeModelForeachFunc)
1396 individual_store_update_list_mode_foreach, self);
1398 g_object_notify (G_OBJECT (self), "show-avatars");
1402 empathy_individual_store_get_show_protocols (EmpathyIndividualStore *self)
1404 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self), TRUE);
1406 return self->priv->show_protocols;
1410 empathy_individual_store_set_show_protocols (EmpathyIndividualStore *self,
1411 gboolean show_protocols)
1413 GtkTreeModel *model;
1415 g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self));
1417 self->priv->show_protocols = show_protocols;
1419 model = GTK_TREE_MODEL (self);
1421 gtk_tree_model_foreach (model,
1422 (GtkTreeModelForeachFunc)
1423 individual_store_update_list_mode_foreach, self);
1425 g_object_notify (G_OBJECT (self), "show-protocols");
1429 empathy_individual_store_get_show_groups (EmpathyIndividualStore *self)
1431 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self), TRUE);
1433 return self->priv->show_groups;
1437 empathy_individual_store_set_show_groups (EmpathyIndividualStore *self,
1438 gboolean show_groups)
1440 EmpathyIndividualStoreClass *klass;
1442 g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self));
1444 klass = EMPATHY_INDIVIDUAL_STORE_GET_CLASS ( self);
1446 if (self->priv->show_groups == show_groups)
1451 self->priv->show_groups = show_groups;
1453 if (!klass->initial_loading (self))
1455 /* Remove all contacts and add them back, not optimized but
1456 * that's the easy way :)
1458 * This is only done if there's not a pending setup idle
1459 * callback, otherwise it will race and the contacts will get
1462 gtk_tree_store_clear (GTK_TREE_STORE (self));
1463 /* Also clear the cache */
1464 g_hash_table_remove_all (self->priv->folks_individual_cache);
1465 g_hash_table_remove_all (self->priv->empathy_group_cache);
1467 klass->reload_individuals (self);
1470 g_object_notify (G_OBJECT (self), "show-groups");
1474 empathy_individual_store_get_is_compact (EmpathyIndividualStore *self)
1476 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self), TRUE);
1478 return self->priv->is_compact;
1482 empathy_individual_store_set_is_compact (EmpathyIndividualStore *self,
1483 gboolean is_compact)
1485 GtkTreeModel *model;
1487 g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self));
1489 self->priv->is_compact = is_compact;
1491 model = GTK_TREE_MODEL (self);
1493 gtk_tree_model_foreach (model,
1494 (GtkTreeModelForeachFunc)
1495 individual_store_update_list_mode_foreach, self);
1497 g_object_notify (G_OBJECT (self), "is-compact");
1500 EmpathyIndividualStoreSort
1501 empathy_individual_store_get_sort_criterium (EmpathyIndividualStore *self)
1503 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self), 0);
1505 return self->priv->sort_criterium;
1509 empathy_individual_store_set_sort_criterium (EmpathyIndividualStore *self,
1510 EmpathyIndividualStoreSort sort_criterium)
1512 g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self));
1514 self->priv->sort_criterium = sort_criterium;
1516 switch (sort_criterium)
1518 case EMPATHY_INDIVIDUAL_STORE_SORT_STATE:
1519 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self),
1520 EMPATHY_INDIVIDUAL_STORE_COL_STATUS, GTK_SORT_ASCENDING);
1523 case EMPATHY_INDIVIDUAL_STORE_SORT_NAME:
1524 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self),
1525 EMPATHY_INDIVIDUAL_STORE_COL_NAME, GTK_SORT_ASCENDING);
1529 g_assert_not_reached ();
1532 g_object_notify (G_OBJECT (self), "sort-criterium");
1536 empathy_individual_store_row_separator_func (GtkTreeModel *model,
1540 gboolean is_separator = FALSE;
1542 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
1544 gtk_tree_model_get (model, iter,
1545 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR, &is_separator, -1);
1547 return is_separator;
1551 empathy_individual_store_get_parent_group (GtkTreeModel *model,
1553 gboolean *path_is_group,
1554 gboolean *is_fake_group)
1556 GtkTreeIter parent_iter, iter;
1559 gboolean fake = FALSE;
1561 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
1565 *path_is_group = FALSE;
1568 if (!gtk_tree_model_get_iter (model, &iter, path))
1573 gtk_tree_model_get (model, &iter,
1574 EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, &is_group,
1575 EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name, -1);
1582 if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter))
1589 gtk_tree_model_get (model, &iter,
1590 EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP, &is_group,
1591 EMPATHY_INDIVIDUAL_STORE_COL_NAME, &name,
1592 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP, &fake, -1);
1602 *path_is_group = TRUE;
1605 if (is_fake_group != NULL)
1606 *is_fake_group = fake;
1612 individual_store_get_individual_status_icon_with_icon_name (
1613 EmpathyIndividualStore *self,
1614 FolksIndividual *individual,
1615 const gchar *status_icon_name)
1617 GdkPixbuf *pixbuf_status;
1618 const gchar *protocol_name = NULL;
1619 gchar *icon_name = NULL;
1622 guint contact_count = 0;
1623 EmpathyContact *contact = NULL;
1624 gboolean show_protocols_here;
1626 personas = folks_individual_get_personas (individual);
1627 iter = gee_iterable_iterator (GEE_ITERABLE (personas));
1628 while (gee_iterator_next (iter))
1630 FolksPersona *persona = gee_iterator_get (iter);
1631 if (empathy_folks_persona_is_interesting (persona))
1634 g_clear_object (&persona);
1636 if (contact_count > 1)
1639 g_clear_object (&iter);
1641 show_protocols_here = (self->priv->show_protocols && (contact_count == 1));
1642 if (show_protocols_here)
1644 contact = empathy_contact_dup_from_folks_individual (individual);
1645 if (contact != NULL)
1647 protocol_name = empathy_protocol_name_for_contact (contact);
1648 icon_name = g_strdup_printf ("%s-%s", status_icon_name,
1653 g_warning ("Cannot retrieve contact from individual '%s'",
1654 folks_alias_details_get_alias (
1655 FOLKS_ALIAS_DETAILS (individual)));
1662 icon_name = g_strdup_printf ("%s", status_icon_name);
1665 pixbuf_status = g_hash_table_lookup (self->priv->status_icons, icon_name);
1667 if (pixbuf_status == NULL)
1670 empathy_pixbuf_contact_status_icon_with_icon_name (contact,
1671 status_icon_name, show_protocols_here);
1673 if (pixbuf_status != NULL)
1675 /* pass the reference to the hash table */
1676 g_hash_table_insert (self->priv->status_icons,
1677 g_strdup (icon_name), pixbuf_status);
1682 tp_clear_object (&contact);
1684 return pixbuf_status;
1688 empathy_individual_store_get_individual_status_icon (
1689 EmpathyIndividualStore *self,
1690 FolksIndividual *individual)
1692 GdkPixbuf *pixbuf_status = NULL;
1693 const gchar *status_icon_name = NULL;
1695 status_icon_name = empathy_icon_name_for_individual (individual);
1696 if (status_icon_name == NULL)
1700 individual_store_get_individual_status_icon_with_icon_name (self,
1701 individual, status_icon_name);
1703 return pixbuf_status;
1707 empathy_individual_store_refresh_individual (EmpathyIndividualStore *self,
1708 FolksIndividual *individual)
1710 gboolean show_active;
1712 show_active = self->priv->show_active;
1713 self->priv->show_active = FALSE;
1714 empathy_individual_store_remove_individual (self, individual);
1715 empathy_individual_store_add_individual (self, individual);
1716 self->priv->show_active = show_active;