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/util.h>
39 #include <libempathy/empathy-utils.h>
40 #include <libempathy/empathy-enum-types.h>
41 #include <libempathy/empathy-individual-manager.h>
43 #include "empathy-individual-store.h"
44 #include "empathy-ui-utils.h"
45 #include "empathy-gtk-enum-types.h"
47 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
48 #include <libempathy/empathy-debug.h>
50 /* Active users are those which have recently changed state
51 * (e.g. online, offline or from normal to a busy state).
54 /* Time in seconds user is shown as active */
55 #define ACTIVE_USER_SHOW_TIME 7
57 /* Time in seconds after connecting which we wait before active users are enabled */
58 #define ACTIVE_USER_WAIT_TO_ENABLE_TIME 5
60 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyIndividualStore)
63 EmpathyIndividualManager
*manager
;
64 gboolean show_avatars
;
67 gboolean show_protocols
;
69 EmpathyIndividualStoreSort sort_criterium
;
72 gboolean dispose_has_run
;
73 GHashTable
*status_icons
;
74 } EmpathyIndividualStorePriv
;
85 FolksIndividual
*individual
;
92 EmpathyIndividualStore
*self
;
93 FolksIndividual
*individual
;
101 PROP_INDIVIDUAL_MANAGER
,
109 /* prototypes to break cycles */
110 static void individual_store_contact_update (EmpathyIndividualStore
*self
,
111 FolksIndividual
*individual
);
113 G_DEFINE_TYPE (EmpathyIndividualStore
, empathy_individual_store
,
114 GTK_TYPE_TREE_STORE
);
117 add_individual_to_store (GtkTreeStore
*self
,
120 FolksIndividual
*individual
,
121 EmpathyIndividualManagerFlags flags
)
123 EmpathyContact
*contact
;
125 contact
= empathy_contact_dup_from_folks_individual (individual
);
127 gtk_tree_store_insert_with_values (self
, iter
, parent
, 0,
128 EMPATHY_INDIVIDUAL_STORE_COL_NAME
,
129 folks_individual_get_alias (individual
),
130 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL
, individual
,
131 EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP
, FALSE
,
132 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR
, FALSE
,
133 EMPATHY_INDIVIDUAL_STORE_COL_FLAGS
, flags
,
138 gtk_tree_store_set (GTK_TREE_STORE (self
), iter
,
139 EMPATHY_INDIVIDUAL_STORE_COL_CAN_AUDIO_CALL
,
140 empathy_contact_get_capabilities (contact
) &
141 EMPATHY_CAPABILITIES_AUDIO
,
142 EMPATHY_INDIVIDUAL_STORE_COL_CAN_VIDEO_CALL
,
143 empathy_contact_get_capabilities (contact
) &
144 EMPATHY_CAPABILITIES_VIDEO
,
148 tp_clear_object (&contact
);
152 individual_store_get_group_foreach (GtkTreeModel
*model
,
160 /* Groups are only at the top level. */
161 if (gtk_tree_path_get_depth (path
) != 1)
164 gtk_tree_model_get (model
, iter
,
165 EMPATHY_INDIVIDUAL_STORE_COL_NAME
, &str
,
166 EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP
, &is_group
, -1);
168 if (is_group
&& !tp_strdiff (str
, fg
->name
))
180 individual_store_get_group (EmpathyIndividualStore
*self
,
182 GtkTreeIter
*iter_group_to_set
,
183 GtkTreeIter
*iter_separator_to_set
,
185 gboolean is_fake_group
)
187 EmpathyIndividualStorePriv
*priv
;
189 GtkTreeIter iter_group
;
190 GtkTreeIter iter_separator
;
193 priv
= GET_PRIV (self
);
195 memset (&fg
, 0, sizeof (fg
));
199 model
= GTK_TREE_MODEL (self
);
200 gtk_tree_model_foreach (model
,
201 (GtkTreeModelForeachFunc
) individual_store_get_group_foreach
, &fg
);
208 gtk_tree_store_insert_with_values (GTK_TREE_STORE (self
), &iter_group
,
210 EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS
, NULL
,
211 EMPATHY_INDIVIDUAL_STORE_COL_NAME
, name
,
212 EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP
, TRUE
,
213 EMPATHY_INDIVIDUAL_STORE_COL_IS_ACTIVE
, FALSE
,
214 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR
, FALSE
,
215 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP
, is_fake_group
,
218 if (iter_group_to_set
)
219 *iter_group_to_set
= iter_group
;
221 gtk_tree_store_insert_with_values (GTK_TREE_STORE (self
), &iter_separator
,
223 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR
, TRUE
,
226 if (iter_separator_to_set
)
227 *iter_separator_to_set
= iter_separator
;
234 if (iter_group_to_set
)
235 *iter_group_to_set
= fg
.iter
;
237 iter_separator
= fg
.iter
;
239 if (gtk_tree_model_iter_next (model
, &iter_separator
))
241 gboolean is_separator
;
243 gtk_tree_model_get (model
, &iter_separator
,
244 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR
, &is_separator
, -1);
246 if (is_separator
&& iter_separator_to_set
)
247 *iter_separator_to_set
= iter_separator
;
253 individual_store_find_contact_foreach (GtkTreeModel
*model
,
258 FolksIndividual
*individual
;
260 gtk_tree_model_get (model
, iter
,
261 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL
, &individual
, -1);
263 if (individual
== fc
->individual
)
266 fc
->iters
= g_list_append (fc
->iters
, gtk_tree_iter_copy (iter
));
269 tp_clear_object (&individual
);
275 individual_store_find_contact (EmpathyIndividualStore
*self
,
276 FolksIndividual
*individual
)
278 EmpathyIndividualStorePriv
*priv
;
283 priv
= GET_PRIV (self
);
285 memset (&fc
, 0, sizeof (fc
));
287 fc
.individual
= individual
;
289 model
= GTK_TREE_MODEL (self
);
290 gtk_tree_model_foreach (model
,
291 (GtkTreeModelForeachFunc
) individual_store_find_contact_foreach
, &fc
);
300 individual_store_remove_individual (EmpathyIndividualStore
*self
,
301 FolksIndividual
*individual
)
303 EmpathyIndividualStorePriv
*priv
;
307 priv
= GET_PRIV (self
);
309 iters
= individual_store_find_contact (self
, individual
);
314 model
= GTK_TREE_MODEL (self
);
316 for (l
= iters
; l
; l
= l
->next
)
320 /* NOTE: it is only <= 2 here because we have
321 * separators after the group name, otherwise it
324 if (gtk_tree_model_iter_parent (model
, &parent
, l
->data
) &&
325 gtk_tree_model_iter_n_children (model
, &parent
) <= 2)
327 gtk_tree_store_remove (GTK_TREE_STORE (self
), &parent
);
331 gtk_tree_store_remove (GTK_TREE_STORE (self
), l
->data
);
335 g_list_foreach (iters
, (GFunc
) gtk_tree_iter_free
, NULL
);
340 individual_store_add_individual (EmpathyIndividualStore
*self
,
341 FolksIndividual
*individual
)
343 EmpathyIndividualStorePriv
*priv
;
345 GHashTable
*group_set
= NULL
;
346 GList
*groups
= NULL
, *l
;
347 EmpathyContact
*contact
;
348 TpConnection
*connection
;
349 EmpathyIndividualManagerFlags flags
= 0;
350 gchar
*protocol_name
;
352 priv
= GET_PRIV (self
);
354 if (EMP_STR_EMPTY (folks_individual_get_alias (individual
)))
357 if (priv
->show_groups
)
359 group_set
= folks_individual_get_groups (individual
);
360 groups
= g_hash_table_get_keys (group_set
);
363 contact
= empathy_contact_dup_from_folks_individual (individual
);
364 connection
= empathy_contact_get_connection (contact
);
365 flags
= empathy_individual_manager_get_flags_for_connection (priv
->manager
,
368 tp_connection_parse_object_path (connection
, &protocol_name
, NULL
);
372 GtkTreeIter iter_group
, *parent
;
374 parent
= &iter_group
;
376 if (!priv
->show_groups
)
378 else if (!tp_strdiff (protocol_name
, "local-xmpp"))
380 /* these are People Nearby */
381 individual_store_get_group (self
,
382 EMPATHY_INDIVIDUAL_STORE_PEOPLE_NEARBY
, &iter_group
, NULL
, NULL
,
387 individual_store_get_group (self
,
388 EMPATHY_INDIVIDUAL_STORE_UNGROUPED
,
389 &iter_group
, NULL
, NULL
, TRUE
);
392 add_individual_to_store (GTK_TREE_STORE (self
), &iter
, parent
,
396 g_free (protocol_name
);
398 /* Else add to each group. */
399 for (l
= groups
; l
; l
= l
->next
)
401 GtkTreeIter iter_group
;
403 individual_store_get_group (self
, l
->data
, &iter_group
, NULL
, NULL
,
406 add_individual_to_store (GTK_TREE_STORE (self
), &iter
, &iter_group
,
409 g_list_free (groups
);
410 if (group_set
!= NULL
)
411 g_hash_table_unref (group_set
);
413 if (priv
->show_groups
&&
414 folks_favourite_get_is_favourite (FOLKS_FAVOURITE (individual
)))
416 /* Add contact to the fake 'Favorites' group */
417 GtkTreeIter iter_group
;
419 individual_store_get_group (self
, EMPATHY_INDIVIDUAL_STORE_FAVORITE
,
420 &iter_group
, NULL
, NULL
, TRUE
);
422 add_individual_to_store (GTK_TREE_STORE (self
), &iter
, &iter_group
,
426 individual_store_contact_update (self
, individual
);
428 tp_clear_object (&contact
);
432 individual_store_contact_set_active (EmpathyIndividualStore
*self
,
433 FolksIndividual
*individual
,
435 gboolean set_changed
)
437 EmpathyIndividualStorePriv
*priv
;
441 priv
= GET_PRIV (self
);
442 model
= GTK_TREE_MODEL (self
);
444 iters
= individual_store_find_contact (self
, individual
);
445 for (l
= iters
; l
; l
= l
->next
)
449 gtk_tree_store_set (GTK_TREE_STORE (self
), l
->data
,
450 EMPATHY_INDIVIDUAL_STORE_COL_IS_ACTIVE
, active
,
453 DEBUG ("Set item %s", active
? "active" : "inactive");
457 path
= gtk_tree_model_get_path (model
, l
->data
);
458 gtk_tree_model_row_changed (model
, path
, l
->data
);
459 gtk_tree_path_free (path
);
463 g_list_foreach (iters
, (GFunc
) gtk_tree_iter_free
, NULL
);
468 static void individual_store_contact_active_free (ShowActiveData
*data
);
471 individual_store_contact_active_invalidated (ShowActiveData
*data
,
474 /* Remove the timeout and free the struct, since the individual or individual
475 * store has disappeared. */
476 g_source_remove (data
->timeout
);
478 if (old_object
== (GObject
*)data
->self
)
480 else if (old_object
== (GObject
*)data
->individual
)
481 data
->individual
= NULL
;
483 g_assert_not_reached ();
485 individual_store_contact_active_free (data
);
488 static ShowActiveData
*
489 individual_store_contact_active_new (EmpathyIndividualStore
*self
,
490 FolksIndividual
*individual
,
493 ShowActiveData
*data
;
495 DEBUG ("Individual'%s' now active, and %s be removed",
496 folks_individual_get_alias (individual
), remove_
? "WILL" : "WILL NOT");
498 data
= g_slice_new0 (ShowActiveData
);
500 /* We don't actually want to force either the IndividualStore or the
501 * Individual to stay alive, since the user could quit Empathy or disable
502 * the account before the contact_active timeout is fired. */
503 g_object_weak_ref (G_OBJECT (self
),
504 (GWeakNotify
) individual_store_contact_active_invalidated
, data
);
505 g_object_weak_ref (G_OBJECT (individual
),
506 (GWeakNotify
) individual_store_contact_active_invalidated
, data
);
509 data
->individual
= individual
;
510 data
->remove
= remove_
;
517 individual_store_contact_active_free (ShowActiveData
*data
)
519 if (data
->self
!= NULL
)
521 g_object_weak_unref (G_OBJECT (data
->self
),
522 (GWeakNotify
) individual_store_contact_active_invalidated
, data
);
525 if (data
->individual
!= NULL
)
527 g_object_weak_unref (G_OBJECT (data
->individual
),
528 (GWeakNotify
) individual_store_contact_active_invalidated
, data
);
531 g_slice_free (ShowActiveData
, data
);
535 individual_store_contact_active_cb (ShowActiveData
*data
)
539 DEBUG ("Individual'%s' active timeout, removing item",
540 folks_individual_get_alias (data
->individual
));
541 individual_store_remove_individual (data
->self
, data
->individual
);
544 DEBUG ("Individual'%s' no longer active",
545 folks_individual_get_alias (data
->individual
));
547 individual_store_contact_set_active (data
->self
,
548 data
->individual
, FALSE
, TRUE
);
550 individual_store_contact_active_free (data
);
556 individual_avatar_pixbuf_received_cb (GObject
*object
,
557 GAsyncResult
*result
,
560 FolksIndividual
*individual
= FOLKS_INDIVIDUAL (object
);
561 EmpathyIndividualStore
*self
= user_data
;
562 GError
*error
= NULL
;
565 pixbuf
= empathy_pixbuf_avatar_from_individual_scaled_finish (individual
,
570 DEBUG ("failed to retrieve pixbuf for individual %s: %s",
571 folks_individual_get_alias (individual
),
573 g_clear_error (&error
);
579 iters
= individual_store_find_contact (self
, individual
);
580 for (l
= iters
; l
; l
= l
->next
)
582 gtk_tree_store_set (GTK_TREE_STORE (self
), l
->data
,
583 EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR
, pixbuf
,
590 individual_store_contact_update (EmpathyIndividualStore
*self
,
591 FolksIndividual
*individual
)
593 EmpathyIndividualStorePriv
*priv
;
594 ShowActiveData
*data
;
596 EmpathyContact
*contact
;
599 gboolean was_online
= TRUE
;
600 gboolean now_online
= FALSE
;
601 gboolean set_model
= FALSE
;
602 gboolean do_remove
= FALSE
;
603 gboolean do_set_active
= FALSE
;
604 gboolean do_set_refresh
= FALSE
;
605 gboolean show_avatar
= FALSE
;
606 GdkPixbuf
*pixbuf_status
;
608 priv
= GET_PRIV (self
);
610 model
= GTK_TREE_MODEL (self
);
611 contact
= empathy_contact_dup_from_folks_individual (individual
);
613 iters
= individual_store_find_contact (self
, individual
);
623 /* Get online state now. */
624 now_online
= folks_individual_is_online (individual
);
628 DEBUG ("Individual'%s' in list:NO, should be:YES",
629 folks_individual_get_alias (individual
));
631 individual_store_add_individual (self
, individual
);
633 if (priv
->show_active
)
635 do_set_active
= TRUE
;
637 DEBUG ("Set active (individual added)");
642 DEBUG ("Individual'%s' in list:YES, should be:YES",
643 folks_individual_get_alias (individual
));
645 /* Get online state before. */
646 if (iters
&& g_list_length (iters
) > 0)
648 gtk_tree_model_get (model
, iters
->data
,
649 EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE
, &was_online
, -1);
652 /* Is this really an update or an online/offline. */
653 if (priv
->show_active
)
655 if (was_online
!= now_online
)
657 do_set_active
= TRUE
;
658 do_set_refresh
= TRUE
;
660 DEBUG ("Set active (individual updated %s)",
661 was_online
? "online -> offline" : "offline -> online");
665 /* Was TRUE for presence updates. */
666 /* do_set_active = FALSE; */
667 do_set_refresh
= TRUE
;
669 DEBUG ("Set active (individual updated)");
676 if (priv
->show_avatars
&& !priv
->is_compact
)
681 empathy_pixbuf_avatar_from_individual_scaled_async (individual
,
682 individual_avatar_pixbuf_received_cb
, 32, 32, self
);
685 empathy_individual_store_get_individual_status_icon (self
, individual
);
687 for (l
= iters
; l
&& set_model
; l
= l
->next
)
689 gtk_tree_store_set (GTK_TREE_STORE (self
), l
->data
,
690 EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS
, pixbuf_status
,
691 EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR_VISIBLE
, show_avatar
,
692 EMPATHY_INDIVIDUAL_STORE_COL_NAME
,
693 folks_individual_get_alias (individual
),
694 EMPATHY_INDIVIDUAL_STORE_COL_PRESENCE_TYPE
,
695 folks_individual_get_presence_type (individual
),
696 EMPATHY_INDIVIDUAL_STORE_COL_STATUS
,
697 folks_individual_get_presence_message (individual
),
698 EMPATHY_INDIVIDUAL_STORE_COL_COMPACT
, priv
->is_compact
,
699 EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP
, FALSE
,
700 EMPATHY_INDIVIDUAL_STORE_COL_IS_ONLINE
, now_online
,
701 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR
, FALSE
,
706 gtk_tree_store_set (GTK_TREE_STORE (self
), l
->data
,
707 EMPATHY_INDIVIDUAL_STORE_COL_CAN_AUDIO_CALL
,
708 empathy_contact_get_capabilities (contact
) &
709 EMPATHY_CAPABILITIES_AUDIO
,
710 EMPATHY_INDIVIDUAL_STORE_COL_CAN_VIDEO_CALL
,
711 empathy_contact_get_capabilities (contact
) &
712 EMPATHY_CAPABILITIES_VIDEO
,
717 if (priv
->show_active
&& do_set_active
)
719 individual_store_contact_set_active (self
, individual
, do_set_active
,
725 individual_store_contact_active_new (self
, individual
,
727 data
->timeout
= g_timeout_add_seconds (ACTIVE_USER_SHOW_TIME
,
728 (GSourceFunc
) individual_store_contact_active_cb
, data
);
732 /* FIXME: when someone goes online then offline quickly, the
733 * first timeout sets the user to be inactive and the second
734 * timeout removes the user from the contact list, really we
735 * should remove the first timeout.
737 g_list_foreach (iters
, (GFunc
) gtk_tree_iter_free
, NULL
);
739 tp_clear_object (&contact
);
743 individual_store_contact_updated_cb (FolksIndividual
*individual
,
745 EmpathyIndividualStore
*self
)
747 DEBUG ("Individual'%s' updated, checking roster is in sync...",
748 folks_individual_get_alias (individual
));
750 individual_store_contact_update (self
, individual
);
754 individual_store_add_individual_and_connect (EmpathyIndividualStore
*self
,
755 FolksIndividual
*individual
)
757 g_signal_connect (individual
, "notify::avatar",
758 G_CALLBACK (individual_store_contact_updated_cb
), self
);
759 g_signal_connect (individual
, "notify::presence-type",
760 G_CALLBACK (individual_store_contact_updated_cb
), self
);
761 g_signal_connect (individual
, "notify::presence-message",
762 G_CALLBACK (individual_store_contact_updated_cb
), self
);
763 g_signal_connect (individual
, "notify::alias",
764 G_CALLBACK (individual_store_contact_updated_cb
), self
);
765 g_signal_connect (individual
, "notify::capabilities",
766 G_CALLBACK (individual_store_contact_updated_cb
), self
);
768 individual_store_add_individual (self
, individual
);
772 individual_store_remove_individual_and_disconnect (
773 EmpathyIndividualStore
*self
,
774 FolksIndividual
*individual
)
776 g_signal_handlers_disconnect_by_func (individual
,
777 G_CALLBACK (individual_store_contact_updated_cb
), self
);
779 individual_store_remove_individual (self
, individual
);
783 individual_store_members_changed_cb (EmpathyIndividualManager
*manager
,
788 EmpathyIndividualStore
*self
)
792 for (l
= added
; l
; l
= l
->next
)
794 DEBUG ("Individual %s %s", folks_individual_get_id (l
->data
), "added");
796 individual_store_add_individual_and_connect (self
, l
->data
);
798 for (l
= removed
; l
; l
= l
->next
)
800 DEBUG ("Individual %s %s",
801 folks_individual_get_id (l
->data
), "removed");
803 individual_store_remove_individual_and_disconnect (self
, l
->data
);
808 individual_store_favourites_changed_cb (EmpathyIndividualManager
*manager
,
809 FolksIndividual
*individual
,
810 gboolean is_favourite
,
811 EmpathyIndividualStore
*self
)
813 EmpathyIndividualStorePriv
*priv
;
815 priv
= GET_PRIV (self
);
817 DEBUG ("Individual %s is %s a favourite",
818 folks_individual_get_id (individual
),
819 is_favourite
? "now" : "no longer");
821 individual_store_remove_individual (self
, individual
);
822 individual_store_add_individual (self
, individual
);
826 individual_store_groups_changed_cb (EmpathyIndividualManager
*manager
,
827 FolksIndividual
*individual
,
830 EmpathyIndividualStore
*self
)
832 EmpathyIndividualStorePriv
*priv
;
833 gboolean show_active
;
835 priv
= GET_PRIV (self
);
837 DEBUG ("Updating groups for individual %s",
838 folks_individual_get_id (individual
));
840 /* We do this to make sure the groups are correct, if not, we
841 * would have to check the groups already set up for each
842 * contact and then see what has been updated.
844 show_active
= priv
->show_active
;
845 priv
->show_active
= FALSE
;
846 individual_store_remove_individual (self
, individual
);
847 individual_store_add_individual (self
, individual
);
848 priv
->show_active
= show_active
;
852 individual_store_manager_setup (gpointer user_data
)
854 EmpathyIndividualStore
*self
= user_data
;
855 EmpathyIndividualStorePriv
*priv
= GET_PRIV (self
);
858 /* Signal connection. */
860 /* TODO: implement */
861 DEBUG ("handling individual renames unimplemented");
863 g_signal_connect (priv
->manager
,
865 G_CALLBACK (individual_store_members_changed_cb
), self
);
867 g_signal_connect (priv
->manager
,
868 "favourites-changed",
869 G_CALLBACK (individual_store_favourites_changed_cb
), self
);
871 g_signal_connect (priv
->manager
,
873 G_CALLBACK (individual_store_groups_changed_cb
), self
);
875 /* Add contacts already created. */
876 individuals
= empathy_individual_manager_get_members (priv
->manager
);
877 if (individuals
!= NULL
&& FOLKS_IS_INDIVIDUAL (individuals
->data
))
879 individual_store_members_changed_cb (priv
->manager
, "initial add",
880 individuals
, NULL
, 0, self
);
881 g_list_free (individuals
);
884 priv
->setup_idle_id
= 0;
889 individual_store_set_individual_manager (EmpathyIndividualStore
*self
,
890 EmpathyIndividualManager
*manager
)
892 EmpathyIndividualStorePriv
*priv
= GET_PRIV (self
);
894 priv
->manager
= g_object_ref (manager
);
896 /* Let a chance to have all properties set before populating */
897 priv
->setup_idle_id
= g_idle_add (individual_store_manager_setup
, self
);
901 individual_store_member_renamed_cb (EmpathyIndividualManager
*manager
,
902 FolksIndividual
*old_individual
,
903 FolksIndividual
*new_individual
,
906 EmpathyIndividualStore
*self
)
908 EmpathyIndividualStorePriv
*priv
;
910 priv
= GET_PRIV (self
);
912 DEBUG ("Individual %s renamed to %s",
913 folks_individual_get_id (old_individual
),
914 folks_individual_get_id (new_individual
));
916 /* add the new contact */
917 individual_store_add_individual_and_connect (self
, new_individual
);
919 /* remove old contact */
920 individual_store_remove_individual_and_disconnect (self
, old_individual
);
924 individual_store_dispose (GObject
*object
)
926 EmpathyIndividualStorePriv
*priv
= GET_PRIV (object
);
929 if (priv
->dispose_has_run
)
931 priv
->dispose_has_run
= TRUE
;
933 contacts
= empathy_individual_manager_get_members (priv
->manager
);
934 for (l
= contacts
; l
; l
= l
->next
)
936 g_signal_handlers_disconnect_by_func (l
->data
,
937 G_CALLBACK (individual_store_contact_updated_cb
), object
);
939 g_list_free (contacts
);
941 g_signal_handlers_disconnect_by_func (priv
->manager
,
942 G_CALLBACK (individual_store_member_renamed_cb
), object
);
943 g_signal_handlers_disconnect_by_func (priv
->manager
,
944 G_CALLBACK (individual_store_members_changed_cb
), object
);
945 g_signal_handlers_disconnect_by_func (priv
->manager
,
946 G_CALLBACK (individual_store_favourites_changed_cb
), object
);
947 g_signal_handlers_disconnect_by_func (priv
->manager
,
948 G_CALLBACK (individual_store_groups_changed_cb
), object
);
949 g_object_unref (priv
->manager
);
951 if (priv
->inhibit_active
)
953 g_source_remove (priv
->inhibit_active
);
956 if (priv
->setup_idle_id
!= 0)
958 g_source_remove (priv
->setup_idle_id
);
961 g_hash_table_destroy (priv
->status_icons
);
962 G_OBJECT_CLASS (empathy_individual_store_parent_class
)->dispose (object
);
966 individual_store_get_property (GObject
*object
,
971 EmpathyIndividualStorePriv
*priv
;
973 priv
= GET_PRIV (object
);
977 case PROP_INDIVIDUAL_MANAGER
:
978 g_value_set_object (value
, priv
->manager
);
980 case PROP_SHOW_AVATARS
:
981 g_value_set_boolean (value
, priv
->show_avatars
);
983 case PROP_SHOW_PROTOCOLS
:
984 g_value_set_boolean (value
, priv
->show_protocols
);
986 case PROP_SHOW_GROUPS
:
987 g_value_set_boolean (value
, priv
->show_groups
);
989 case PROP_IS_COMPACT
:
990 g_value_set_boolean (value
, priv
->is_compact
);
992 case PROP_SORT_CRITERIUM
:
993 g_value_set_enum (value
, priv
->sort_criterium
);
996 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
1002 individual_store_set_property (GObject
*object
,
1004 const GValue
*value
,
1007 EmpathyIndividualStorePriv
*priv
;
1009 priv
= GET_PRIV (object
);
1013 case PROP_INDIVIDUAL_MANAGER
:
1014 individual_store_set_individual_manager (EMPATHY_INDIVIDUAL_STORE
1015 (object
), g_value_get_object (value
));
1017 case PROP_SHOW_AVATARS
:
1018 empathy_individual_store_set_show_avatars (EMPATHY_INDIVIDUAL_STORE
1019 (object
), g_value_get_boolean (value
));
1021 case PROP_SHOW_PROTOCOLS
:
1022 empathy_individual_store_set_show_protocols (EMPATHY_INDIVIDUAL_STORE
1023 (object
), g_value_get_boolean (value
));
1025 case PROP_SHOW_GROUPS
:
1026 empathy_individual_store_set_show_groups (EMPATHY_INDIVIDUAL_STORE
1027 (object
), g_value_get_boolean (value
));
1029 case PROP_IS_COMPACT
:
1030 empathy_individual_store_set_is_compact (EMPATHY_INDIVIDUAL_STORE
1031 (object
), g_value_get_boolean (value
));
1033 case PROP_SORT_CRITERIUM
:
1034 empathy_individual_store_set_sort_criterium (EMPATHY_INDIVIDUAL_STORE
1035 (object
), g_value_get_enum (value
));
1038 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, param_id
, pspec
);
1044 empathy_individual_store_class_init (EmpathyIndividualStoreClass
*klass
)
1046 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
1048 object_class
->dispose
= individual_store_dispose
;
1049 object_class
->get_property
= individual_store_get_property
;
1050 object_class
->set_property
= individual_store_set_property
;
1052 g_object_class_install_property (object_class
,
1053 PROP_INDIVIDUAL_MANAGER
,
1054 g_param_spec_object ("individual-manager",
1055 "The individual manager",
1056 "The individual manager",
1057 EMPATHY_TYPE_INDIVIDUAL_MANAGER
,
1058 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
1059 g_object_class_install_property (object_class
,
1061 g_param_spec_boolean ("show-avatars",
1063 "Whether contact list should display "
1064 "avatars for contacts", TRUE
, G_PARAM_READWRITE
));
1065 g_object_class_install_property (object_class
,
1066 PROP_SHOW_PROTOCOLS
,
1067 g_param_spec_boolean ("show-protocols",
1069 "Whether contact list should display "
1070 "protocols for contacts", FALSE
, G_PARAM_READWRITE
));
1071 g_object_class_install_property (object_class
,
1073 g_param_spec_boolean ("show-groups",
1075 "Whether contact list should display "
1076 "contact groups", TRUE
, G_PARAM_READWRITE
));
1077 g_object_class_install_property (object_class
,
1079 g_param_spec_boolean ("is-compact",
1081 "Whether the contact list is in compact mode or not",
1082 FALSE
, G_PARAM_READWRITE
));
1084 g_object_class_install_property (object_class
,
1085 PROP_SORT_CRITERIUM
,
1086 g_param_spec_enum ("sort-criterium",
1088 "The sort criterium to use for sorting the contact list",
1089 EMPATHY_TYPE_INDIVIDUAL_STORE_SORT
,
1090 EMPATHY_INDIVIDUAL_STORE_SORT_NAME
, G_PARAM_READWRITE
));
1092 g_type_class_add_private (object_class
,
1093 sizeof (EmpathyIndividualStorePriv
));
1097 get_position (const char **strv
,
1102 for (i
= 0; strv
[i
] != NULL
; i
++)
1104 if (!tp_strdiff (strv
[i
], str
))
1112 compare_separator_and_groups (gboolean is_separator_a
,
1113 gboolean is_separator_b
,
1114 const gchar
*name_a
,
1115 const gchar
*name_b
,
1116 FolksIndividual
*individual_a
,
1117 FolksIndividual
*individual_b
,
1118 gboolean fake_group_a
,
1119 gboolean fake_group_b
)
1121 /* these two lists are the sorted list of fake groups to include at the
1122 * top and bottom of the roster */
1123 const char *top_groups
[] = {
1124 EMPATHY_INDIVIDUAL_STORE_FAVORITE
,
1128 const char *bottom_groups
[] = {
1129 EMPATHY_INDIVIDUAL_STORE_UNGROUPED
,
1133 if (is_separator_a
|| is_separator_b
)
1135 /* We have at least one separator */
1140 else if (is_separator_b
)
1146 /* One group and one contact */
1147 if (!individual_a
&& individual_b
)
1151 else if (individual_a
&& !individual_b
)
1155 else if (!individual_a
&& !individual_b
)
1157 gboolean a_in_top
, b_in_top
, a_in_bottom
, b_in_bottom
;
1159 a_in_top
= fake_group_a
&& tp_strv_contains (top_groups
, name_a
);
1160 b_in_top
= fake_group_b
&& tp_strv_contains (top_groups
, name_b
);
1161 a_in_bottom
= fake_group_a
&& tp_strv_contains (bottom_groups
, name_a
);
1162 b_in_bottom
= fake_group_b
&& tp_strv_contains (bottom_groups
, name_b
);
1164 if (a_in_top
&& b_in_top
)
1166 /* compare positions */
1167 return CLAMP (get_position (top_groups
, name_a
) -
1168 get_position (top_groups
, name_b
), -1, 1);
1170 else if (a_in_bottom
&& b_in_bottom
)
1172 /* compare positions */
1173 return CLAMP (get_position (bottom_groups
, name_a
) -
1174 get_position (bottom_groups
, name_b
), -1, 1);
1176 else if (a_in_top
|| b_in_bottom
)
1180 else if (b_in_top
|| a_in_bottom
)
1186 return g_utf8_collate (name_a
, name_b
);
1190 /* Two contacts, ordering depends of the sorting policy */
1195 individual_store_contact_sort (FolksIndividual
*individual_a
,
1196 FolksIndividual
*individual_b
)
1199 EmpathyContact
*contact_a
= NULL
, *contact_b
= NULL
;
1200 TpAccount
*account_a
, *account_b
;
1202 g_return_val_if_fail (individual_a
!= NULL
|| individual_b
!= NULL
, 0);
1205 ret_val
= g_utf8_collate (folks_individual_get_alias (individual_a
),
1206 folks_individual_get_alias (individual_b
));
1211 contact_a
= empathy_contact_dup_from_folks_individual (individual_a
);
1212 contact_b
= empathy_contact_dup_from_folks_individual (individual_b
);
1213 account_a
= empathy_contact_get_account (contact_a
);
1214 account_b
= empathy_contact_get_account (contact_b
);
1217 ret_val
= g_strcmp0 (tp_account_get_protocol (account_a
),
1218 tp_account_get_protocol (account_b
));
1224 ret_val
= g_strcmp0 (tp_proxy_get_object_path (account_a
),
1225 tp_proxy_get_object_path (account_b
));
1231 ret_val
= g_utf8_collate (folks_individual_get_id (individual_a
),
1232 folks_individual_get_id (individual_b
));
1235 tp_clear_object (&contact_a
);
1236 tp_clear_object (&contact_b
);
1242 individual_store_state_sort_func (GtkTreeModel
*model
,
1243 GtkTreeIter
*iter_a
,
1244 GtkTreeIter
*iter_b
,
1248 FolksIndividual
*individual_a
, *individual_b
;
1249 gchar
*name_a
, *name_b
;
1250 gboolean is_separator_a
, is_separator_b
;
1251 gboolean fake_group_a
, fake_group_b
;
1252 FolksPresenceType folks_presence_type_a
, folks_presence_type_b
;
1253 TpConnectionPresenceType tp_presence_a
, tp_presence_b
;
1255 gtk_tree_model_get (model
, iter_a
,
1256 EMPATHY_INDIVIDUAL_STORE_COL_NAME
, &name_a
,
1257 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL
, &individual_a
,
1258 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR
, &is_separator_a
,
1259 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP
, &fake_group_a
, -1);
1260 gtk_tree_model_get (model
, iter_b
,
1261 EMPATHY_INDIVIDUAL_STORE_COL_NAME
, &name_b
,
1262 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL
, &individual_b
,
1263 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR
, &is_separator_b
,
1264 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP
, &fake_group_b
, -1);
1266 if (individual_a
== NULL
|| individual_b
== NULL
)
1268 ret_val
= compare_separator_and_groups (is_separator_a
, is_separator_b
,
1269 name_a
, name_b
, individual_a
, individual_b
, fake_group_a
,
1274 /* If we managed to get this far, we can start looking at
1277 folks_presence_type_a
= folks_individual_get_presence_type (individual_a
);
1278 folks_presence_type_b
= folks_individual_get_presence_type (individual_b
);
1279 tp_presence_a
= empathy_folks_presence_type_to_tp (folks_presence_type_a
);
1280 tp_presence_b
= empathy_folks_presence_type_to_tp (folks_presence_type_b
);
1282 ret_val
= -tp_connection_presence_type_cmp_availability (tp_presence_a
,
1287 /* Fallback: compare by name et al. */
1288 ret_val
= individual_store_contact_sort (individual_a
, individual_b
);
1294 tp_clear_object (&individual_a
);
1295 tp_clear_object (&individual_b
);
1301 individual_store_name_sort_func (GtkTreeModel
*model
,
1302 GtkTreeIter
*iter_a
,
1303 GtkTreeIter
*iter_b
,
1306 gchar
*name_a
, *name_b
;
1307 FolksIndividual
*individual_a
, *individual_b
;
1308 gboolean is_separator_a
= FALSE
, is_separator_b
= FALSE
;
1310 gboolean fake_group_a
, fake_group_b
;
1312 gtk_tree_model_get (model
, iter_a
,
1313 EMPATHY_INDIVIDUAL_STORE_COL_NAME
, &name_a
,
1314 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL
, &individual_a
,
1315 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR
, &is_separator_a
,
1316 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP
, &fake_group_a
, -1);
1317 gtk_tree_model_get (model
, iter_b
,
1318 EMPATHY_INDIVIDUAL_STORE_COL_NAME
, &name_b
,
1319 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL
, &individual_b
,
1320 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR
, &is_separator_b
,
1321 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP
, &fake_group_b
, -1);
1323 if (individual_a
== NULL
|| individual_b
== NULL
)
1324 ret_val
= compare_separator_and_groups (is_separator_a
, is_separator_b
,
1325 name_a
, name_b
, individual_a
, individual_b
, fake_group_a
, fake_group_b
);
1327 ret_val
= individual_store_contact_sort (individual_a
, individual_b
);
1329 tp_clear_object (&individual_a
);
1330 tp_clear_object (&individual_b
);
1336 individual_store_setup (EmpathyIndividualStore
*self
)
1338 EmpathyIndividualStorePriv
*priv
;
1340 GDK_TYPE_PIXBUF
, /* Status pixbuf */
1341 GDK_TYPE_PIXBUF
, /* Avatar pixbuf */
1342 G_TYPE_BOOLEAN
, /* Avatar pixbuf visible */
1343 G_TYPE_STRING
, /* Name */
1344 G_TYPE_UINT
, /* Presence type */
1345 G_TYPE_STRING
, /* Status string */
1346 G_TYPE_BOOLEAN
, /* Compact view */
1347 FOLKS_TYPE_INDIVIDUAL
, /* Individual type */
1348 G_TYPE_BOOLEAN
, /* Is group */
1349 G_TYPE_BOOLEAN
, /* Is active */
1350 G_TYPE_BOOLEAN
, /* Is online */
1351 G_TYPE_BOOLEAN
, /* Is separator */
1352 G_TYPE_BOOLEAN
, /* Can make audio calls */
1353 G_TYPE_BOOLEAN
, /* Can make video calls */
1354 EMPATHY_TYPE_INDIVIDUAL_MANAGER_FLAGS
, /* Flags */
1355 G_TYPE_BOOLEAN
, /* Is a fake group */
1358 priv
= GET_PRIV (self
);
1360 gtk_tree_store_set_column_types (GTK_TREE_STORE (self
),
1361 EMPATHY_INDIVIDUAL_STORE_COL_COUNT
, types
);
1363 /* Set up sorting */
1364 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (self
),
1365 EMPATHY_INDIVIDUAL_STORE_COL_NAME
,
1366 individual_store_name_sort_func
, self
, NULL
);
1367 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (self
),
1368 EMPATHY_INDIVIDUAL_STORE_COL_STATUS
,
1369 individual_store_state_sort_func
, self
, NULL
);
1371 priv
->sort_criterium
= EMPATHY_INDIVIDUAL_STORE_SORT_NAME
;
1372 empathy_individual_store_set_sort_criterium (self
, priv
->sort_criterium
);
1376 individual_store_inhibit_active_cb (EmpathyIndividualStore
*self
)
1378 EmpathyIndividualStorePriv
*priv
;
1380 priv
= GET_PRIV (self
);
1382 priv
->show_active
= TRUE
;
1383 priv
->inhibit_active
= 0;
1389 empathy_individual_store_init (EmpathyIndividualStore
*self
)
1391 EmpathyIndividualStorePriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (self
,
1392 EMPATHY_TYPE_INDIVIDUAL_STORE
, EmpathyIndividualStorePriv
);
1395 priv
->show_avatars
= TRUE
;
1396 priv
->show_groups
= TRUE
;
1397 priv
->show_protocols
= FALSE
;
1398 priv
->inhibit_active
=
1399 g_timeout_add_seconds (ACTIVE_USER_WAIT_TO_ENABLE_TIME
,
1400 (GSourceFunc
) individual_store_inhibit_active_cb
, self
);
1401 priv
->status_icons
=
1402 g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_object_unref
);
1403 individual_store_setup (self
);
1406 EmpathyIndividualStore
*
1407 empathy_individual_store_new (EmpathyIndividualManager
*manager
)
1409 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (manager
), NULL
);
1411 return g_object_new (EMPATHY_TYPE_INDIVIDUAL_STORE
,
1412 "individual-manager", manager
, NULL
);
1415 EmpathyIndividualManager
*
1416 empathy_individual_store_get_manager (EmpathyIndividualStore
*self
)
1418 EmpathyIndividualStorePriv
*priv
;
1420 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self
), FALSE
);
1422 priv
= GET_PRIV (self
);
1424 return priv
->manager
;
1428 empathy_individual_store_get_show_avatars (EmpathyIndividualStore
*self
)
1430 EmpathyIndividualStorePriv
*priv
;
1432 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self
), TRUE
);
1434 priv
= GET_PRIV (self
);
1436 return priv
->show_avatars
;
1440 individual_store_update_list_mode_foreach (GtkTreeModel
*model
,
1443 EmpathyIndividualStore
*self
)
1445 EmpathyIndividualStorePriv
*priv
;
1446 gboolean show_avatar
= FALSE
;
1447 FolksIndividual
*individual
;
1448 GdkPixbuf
*pixbuf_status
;
1450 priv
= GET_PRIV (self
);
1452 if (priv
->show_avatars
&& !priv
->is_compact
)
1457 gtk_tree_model_get (model
, iter
,
1458 EMPATHY_INDIVIDUAL_STORE_COL_INDIVIDUAL
, &individual
, -1);
1460 if (individual
== NULL
)
1464 /* get icon from hash_table */
1466 empathy_individual_store_get_individual_status_icon (self
, individual
);
1468 gtk_tree_store_set (GTK_TREE_STORE (self
), iter
,
1469 EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS
, pixbuf_status
,
1470 EMPATHY_INDIVIDUAL_STORE_COL_PIXBUF_AVATAR_VISIBLE
, show_avatar
,
1471 EMPATHY_INDIVIDUAL_STORE_COL_COMPACT
, priv
->is_compact
, -1);
1473 g_object_unref (individual
);
1479 empathy_individual_store_set_show_avatars (EmpathyIndividualStore
*self
,
1480 gboolean show_avatars
)
1482 EmpathyIndividualStorePriv
*priv
;
1483 GtkTreeModel
*model
;
1485 g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self
));
1487 priv
= GET_PRIV (self
);
1489 priv
->show_avatars
= show_avatars
;
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
), "show-avatars");
1501 empathy_individual_store_get_show_protocols (EmpathyIndividualStore
*self
)
1503 EmpathyIndividualStorePriv
*priv
;
1505 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self
), TRUE
);
1507 priv
= GET_PRIV (self
);
1509 return priv
->show_protocols
;
1513 empathy_individual_store_set_show_protocols (EmpathyIndividualStore
*self
,
1514 gboolean show_protocols
)
1516 EmpathyIndividualStorePriv
*priv
;
1517 GtkTreeModel
*model
;
1519 g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self
));
1521 priv
= GET_PRIV (self
);
1523 priv
->show_protocols
= show_protocols
;
1525 model
= GTK_TREE_MODEL (self
);
1527 gtk_tree_model_foreach (model
,
1528 (GtkTreeModelForeachFunc
)
1529 individual_store_update_list_mode_foreach
, self
);
1531 g_object_notify (G_OBJECT (self
), "show-protocols");
1535 empathy_individual_store_get_show_groups (EmpathyIndividualStore
*self
)
1537 EmpathyIndividualStorePriv
*priv
;
1539 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self
), TRUE
);
1541 priv
= GET_PRIV (self
);
1543 return priv
->show_groups
;
1547 empathy_individual_store_set_show_groups (EmpathyIndividualStore
*self
,
1548 gboolean show_groups
)
1550 EmpathyIndividualStorePriv
*priv
;
1552 g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self
));
1554 priv
= GET_PRIV (self
);
1556 if (priv
->show_groups
== show_groups
)
1561 priv
->show_groups
= show_groups
;
1563 if (priv
->setup_idle_id
== 0)
1565 /* Remove all contacts and add them back, not optimized but
1566 * that's the easy way :)
1568 * This is only done if there's not a pending setup idle
1569 * callback, otherwise it will race and the contacts will get
1573 gtk_tree_store_clear (GTK_TREE_STORE (self
));
1574 contacts
= empathy_individual_manager_get_members (priv
->manager
);
1576 individual_store_members_changed_cb (priv
->manager
,
1577 "re-adding members: toggled group visibility",
1578 contacts
, NULL
, 0, self
);
1579 g_list_free (contacts
);
1582 g_object_notify (G_OBJECT (self
), "show-groups");
1586 empathy_individual_store_get_is_compact (EmpathyIndividualStore
*self
)
1588 EmpathyIndividualStorePriv
*priv
;
1590 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self
), TRUE
);
1592 priv
= GET_PRIV (self
);
1594 return priv
->is_compact
;
1598 empathy_individual_store_set_is_compact (EmpathyIndividualStore
*self
,
1599 gboolean is_compact
)
1601 EmpathyIndividualStorePriv
*priv
;
1602 GtkTreeModel
*model
;
1604 g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self
));
1606 priv
= GET_PRIV (self
);
1608 priv
->is_compact
= is_compact
;
1610 model
= GTK_TREE_MODEL (self
);
1612 gtk_tree_model_foreach (model
,
1613 (GtkTreeModelForeachFunc
)
1614 individual_store_update_list_mode_foreach
, self
);
1616 g_object_notify (G_OBJECT (self
), "is-compact");
1619 EmpathyIndividualStoreSort
1620 empathy_individual_store_get_sort_criterium (EmpathyIndividualStore
*self
)
1622 EmpathyIndividualStorePriv
*priv
;
1624 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self
), 0);
1626 priv
= GET_PRIV (self
);
1628 return priv
->sort_criterium
;
1632 empathy_individual_store_set_sort_criterium (EmpathyIndividualStore
*self
,
1633 EmpathyIndividualStoreSort sort_criterium
)
1635 EmpathyIndividualStorePriv
*priv
;
1637 g_return_if_fail (EMPATHY_IS_INDIVIDUAL_STORE (self
));
1639 priv
= GET_PRIV (self
);
1641 priv
->sort_criterium
= sort_criterium
;
1643 switch (sort_criterium
)
1645 case EMPATHY_INDIVIDUAL_STORE_SORT_STATE
:
1646 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self
),
1647 EMPATHY_INDIVIDUAL_STORE_COL_STATUS
, GTK_SORT_ASCENDING
);
1650 case EMPATHY_INDIVIDUAL_STORE_SORT_NAME
:
1651 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (self
),
1652 EMPATHY_INDIVIDUAL_STORE_COL_NAME
, GTK_SORT_ASCENDING
);
1656 g_assert_not_reached ();
1659 g_object_notify (G_OBJECT (self
), "sort-criterium");
1663 empathy_individual_store_row_separator_func (GtkTreeModel
*model
,
1667 gboolean is_separator
= FALSE
;
1669 g_return_val_if_fail (GTK_IS_TREE_MODEL (model
), FALSE
);
1671 gtk_tree_model_get (model
, iter
,
1672 EMPATHY_INDIVIDUAL_STORE_COL_IS_SEPARATOR
, &is_separator
, -1);
1674 return is_separator
;
1678 empathy_individual_store_get_parent_group (GtkTreeModel
*model
,
1680 gboolean
*path_is_group
,
1681 gboolean
*is_fake_group
)
1683 GtkTreeIter parent_iter
, iter
;
1688 g_return_val_if_fail (GTK_IS_TREE_MODEL (model
), NULL
);
1692 *path_is_group
= FALSE
;
1695 if (!gtk_tree_model_get_iter (model
, &iter
, path
))
1700 gtk_tree_model_get (model
, &iter
,
1701 EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP
, &is_group
,
1702 EMPATHY_INDIVIDUAL_STORE_COL_NAME
, &name
, -1);
1709 if (!gtk_tree_model_iter_parent (model
, &parent_iter
, &iter
))
1716 gtk_tree_model_get (model
, &iter
,
1717 EMPATHY_INDIVIDUAL_STORE_COL_IS_GROUP
, &is_group
,
1718 EMPATHY_INDIVIDUAL_STORE_COL_NAME
, &name
,
1719 EMPATHY_INDIVIDUAL_STORE_COL_IS_FAKE_GROUP
, &fake
, -1);
1729 *path_is_group
= TRUE
;
1732 if (is_fake_group
!= NULL
)
1733 *is_fake_group
= fake
;
1739 individual_store_get_individual_status_icon_with_icon_name (
1740 EmpathyIndividualStore
*self
,
1741 FolksIndividual
*individual
,
1742 const gchar
*status_icon_name
)
1744 GdkPixbuf
*pixbuf_status
= NULL
;
1745 EmpathyIndividualStorePriv
*priv
;
1746 const gchar
*protocol_name
= NULL
;
1747 gchar
*icon_name
= NULL
;
1748 GList
*personas
, *l
;
1749 guint contact_count
;
1750 EmpathyContact
*contact
= NULL
;
1751 gboolean show_protocols_here
;
1753 priv
= GET_PRIV (self
);
1755 personas
= folks_individual_get_personas (individual
);
1756 for (l
= personas
, contact_count
= 0; l
; l
= l
->next
)
1758 if (TPF_IS_PERSONA (l
->data
))
1761 if (contact_count
> 1)
1765 show_protocols_here
= (priv
->show_protocols
&& (contact_count
== 1));
1766 if (show_protocols_here
)
1768 contact
= empathy_contact_dup_from_folks_individual (individual
);
1769 protocol_name
= empathy_protocol_name_for_contact (contact
);
1770 icon_name
= g_strdup_printf ("%s-%s", status_icon_name
, protocol_name
);
1774 icon_name
= g_strdup_printf ("%s", status_icon_name
);
1776 if (pixbuf_status
== NULL
)
1779 empathy_pixbuf_contact_status_icon_with_icon_name (contact
,
1780 status_icon_name
, priv
->show_protocols
);
1781 if (pixbuf_status
!= NULL
)
1783 g_hash_table_insert (priv
->status_icons
,
1784 g_strdup (icon_name
), pixbuf_status
);
1789 tp_clear_object (&contact
);
1791 return pixbuf_status
;
1795 empathy_individual_store_get_individual_status_icon (
1796 EmpathyIndividualStore
*self
,
1797 FolksIndividual
*individual
)
1799 GdkPixbuf
*pixbuf_status
= NULL
;
1800 const gchar
*status_icon_name
= NULL
;
1802 status_icon_name
= empathy_icon_name_for_individual (individual
);
1803 if (status_icon_name
== NULL
)
1807 individual_store_get_individual_status_icon_with_icon_name (self
,
1808 individual
, status_icon_name
);
1810 return pixbuf_status
;