4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 #include "buddylist.h"
25 #include "conversation.h"
36 /* Private data for a buddy list. */
38 PurpleBlistNode
*root
;
39 GHashTable
*buddies
; /* Every buddy in this list */
40 } PurpleBuddyListPrivate
;
42 static GType buddy_list_type
= G_TYPE_INVALID
;
43 static PurpleBuddyList
*purplebuddylist
= NULL
;
45 G_DEFINE_TYPE_WITH_PRIVATE(PurpleBuddyList
, purple_buddy_list
, G_TYPE_OBJECT
);
48 * A hash table used for efficient lookups of buddies by name.
49 * PurpleAccount* => GHashTable*, with the inner hash table being
50 * struct _purple_hbuddy => PurpleBuddy*
52 static GHashTable
*buddies_cache
= NULL
;
55 * A hash table used for efficient lookups of groups by name.
56 * UTF-8 collate-key => PurpleGroup*.
58 static GHashTable
*groups_cache
= NULL
;
60 static guint save_timer
= 0;
61 static gboolean blist_loaded
= FALSE
;
62 static gchar
*localized_default_group_name
= NULL
;
64 /*********************************************************************
65 * Private utility functions *
66 *********************************************************************/
69 purple_blist_fold_name(const gchar
*name
)
76 tmp
= g_utf8_casefold(name
, -1);
77 res
= g_utf8_collate_key(tmp
, -1);
83 static PurpleBlistNode
*purple_blist_get_last_sibling(PurpleBlistNode
*node
)
85 PurpleBlistNode
*n
= node
;
93 PurpleBlistNode
*_purple_blist_get_last_child(PurpleBlistNode
*node
)
97 return purple_blist_get_last_sibling(node
->child
);
100 struct _list_account_buddies
{
102 PurpleAccount
*account
;
105 struct _purple_hbuddy
{
107 PurpleAccount
*account
;
108 PurpleBlistNode
*group
;
111 /* This function must not use purple_normalize */
112 static guint
_purple_blist_hbuddy_hash(struct _purple_hbuddy
*hb
)
114 return g_str_hash(hb
->name
) ^ g_direct_hash(hb
->group
) ^ g_direct_hash(hb
->account
);
117 /* This function must not use purple_normalize */
118 static guint
_purple_blist_hbuddy_equal(struct _purple_hbuddy
*hb1
, struct _purple_hbuddy
*hb2
)
120 return (hb1
->group
== hb2
->group
&&
121 hb1
->account
== hb2
->account
&&
122 purple_strequal(hb1
->name
, hb2
->name
));
125 static void _purple_blist_hbuddy_free_key(struct _purple_hbuddy
*hb
)
132 purple_blist_buddies_cache_add_account(PurpleAccount
*account
)
134 GHashTable
*account_buddies
= g_hash_table_new_full((GHashFunc
)_purple_blist_hbuddy_hash
,
135 (GEqualFunc
)_purple_blist_hbuddy_equal
,
136 (GDestroyNotify
)_purple_blist_hbuddy_free_key
, NULL
);
137 g_hash_table_insert(buddies_cache
, account
, account_buddies
);
141 purple_blist_buddies_cache_remove_account(const PurpleAccount
*account
)
143 g_hash_table_remove(buddies_cache
, account
);
146 /*********************************************************************
148 *********************************************************************/
151 value_to_xmlnode(gpointer key
, gpointer hvalue
, gpointer user_data
)
155 PurpleXmlNode
*node
, *child
;
158 name
= (const char *)key
;
159 value
= (GValue
*)hvalue
;
160 node
= (PurpleXmlNode
*)user_data
;
162 g_return_if_fail(value
!= NULL
);
164 child
= purple_xmlnode_new_child(node
, "setting");
165 purple_xmlnode_set_attrib(child
, "name", name
);
167 if (G_VALUE_HOLDS_INT(value
)) {
168 purple_xmlnode_set_attrib(child
, "type", "int");
169 g_snprintf(buf
, sizeof(buf
), "%d", g_value_get_int(value
));
170 purple_xmlnode_insert_data(child
, buf
, -1);
172 else if (G_VALUE_HOLDS_STRING(value
)) {
173 purple_xmlnode_set_attrib(child
, "type", "string");
174 purple_xmlnode_insert_data(child
, g_value_get_string(value
), -1);
176 else if (G_VALUE_HOLDS_BOOLEAN(value
)) {
177 purple_xmlnode_set_attrib(child
, "type", "bool");
178 g_snprintf(buf
, sizeof(buf
), "%d", g_value_get_boolean(value
));
179 purple_xmlnode_insert_data(child
, buf
, -1);
184 chat_component_to_xmlnode(gpointer key
, gpointer value
, gpointer user_data
)
188 PurpleXmlNode
*node
, *child
;
190 name
= (const char *)key
;
191 data
= (const char *)value
;
192 node
= (PurpleXmlNode
*)user_data
;
194 g_return_if_fail(data
!= NULL
);
196 child
= purple_xmlnode_new_child(node
, "component");
197 purple_xmlnode_set_attrib(child
, "name", name
);
198 purple_xmlnode_insert_data(child
, data
, -1);
201 static PurpleXmlNode
*
202 buddy_to_xmlnode(PurpleBuddy
*buddy
)
204 PurpleXmlNode
*node
, *child
;
205 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
206 const char *alias
= purple_buddy_get_local_alias(buddy
);
208 node
= purple_xmlnode_new("buddy");
209 purple_xmlnode_set_attrib(node
, "account", purple_account_get_username(account
));
210 purple_xmlnode_set_attrib(node
, "proto", purple_account_get_protocol_id(account
));
212 child
= purple_xmlnode_new_child(node
, "name");
213 purple_xmlnode_insert_data(child
, purple_buddy_get_name(buddy
), -1);
217 child
= purple_xmlnode_new_child(node
, "alias");
218 purple_xmlnode_insert_data(child
, alias
, -1);
221 /* Write buddy settings */
222 g_hash_table_foreach(purple_blist_node_get_settings(PURPLE_BLIST_NODE(buddy
)),
223 value_to_xmlnode
, node
);
228 static PurpleXmlNode
*
229 contact_to_xmlnode(PurpleContact
*contact
)
231 PurpleXmlNode
*node
, *child
;
232 PurpleBlistNode
*bnode
;
235 node
= purple_xmlnode_new("contact");
236 g_object_get(contact
, "alias", &alias
, NULL
);
240 purple_xmlnode_set_attrib(node
, "alias", alias
);
244 for (bnode
= PURPLE_BLIST_NODE(contact
)->child
; bnode
!= NULL
; bnode
= bnode
->next
)
246 if (purple_blist_node_is_transient(bnode
))
248 if (PURPLE_IS_BUDDY(bnode
))
250 child
= buddy_to_xmlnode(PURPLE_BUDDY(bnode
));
251 purple_xmlnode_insert_child(node
, child
);
255 /* Write contact settings */
256 g_hash_table_foreach(purple_blist_node_get_settings(PURPLE_BLIST_NODE(contact
)),
257 value_to_xmlnode
, node
);
263 static PurpleXmlNode
*
264 chat_to_xmlnode(PurpleChat
*chat
)
266 PurpleXmlNode
*node
, *child
;
267 PurpleAccount
*account
= purple_chat_get_account(chat
);
270 g_object_get(chat
, "alias", &alias
, NULL
);
272 node
= purple_xmlnode_new("chat");
273 purple_xmlnode_set_attrib(node
, "proto", purple_account_get_protocol_id(account
));
274 purple_xmlnode_set_attrib(node
, "account", purple_account_get_username(account
));
278 child
= purple_xmlnode_new_child(node
, "alias");
279 purple_xmlnode_insert_data(child
, alias
, -1);
282 /* Write chat components */
283 g_hash_table_foreach(purple_chat_get_components(chat
),
284 chat_component_to_xmlnode
, node
);
286 /* Write chat settings */
287 g_hash_table_foreach(purple_blist_node_get_settings(PURPLE_BLIST_NODE(chat
)),
288 value_to_xmlnode
, node
);
294 static PurpleXmlNode
*
295 group_to_xmlnode(PurpleGroup
*group
)
297 PurpleXmlNode
*node
, *child
;
298 PurpleBlistNode
*cnode
;
300 node
= purple_xmlnode_new("group");
301 if (group
!= purple_blist_get_default_group())
302 purple_xmlnode_set_attrib(node
, "name", purple_group_get_name(group
));
305 g_hash_table_foreach(purple_blist_node_get_settings(PURPLE_BLIST_NODE(group
)),
306 value_to_xmlnode
, node
);
308 /* Write contacts and chats */
309 for (cnode
= PURPLE_BLIST_NODE(group
)->child
; cnode
!= NULL
; cnode
= cnode
->next
)
311 if (purple_blist_node_is_transient(cnode
))
313 if (PURPLE_IS_CONTACT(cnode
))
315 child
= contact_to_xmlnode(PURPLE_CONTACT(cnode
));
316 purple_xmlnode_insert_child(node
, child
);
318 else if (PURPLE_IS_CHAT(cnode
))
320 child
= chat_to_xmlnode(PURPLE_CHAT(cnode
));
321 purple_xmlnode_insert_child(node
, child
);
328 static PurpleXmlNode
*
329 accountprivacy_to_xmlnode(PurpleAccount
*account
)
331 PurpleXmlNode
*node
, *child
;
335 node
= purple_xmlnode_new("account");
336 purple_xmlnode_set_attrib(node
, "proto", purple_account_get_protocol_id(account
));
337 purple_xmlnode_set_attrib(node
, "name", purple_account_get_username(account
));
338 g_snprintf(buf
, sizeof(buf
), "%d", purple_account_get_privacy_type(account
));
339 purple_xmlnode_set_attrib(node
, "mode", buf
);
341 for (cur
= purple_account_privacy_get_permitted(account
); cur
; cur
= cur
->next
)
343 child
= purple_xmlnode_new_child(node
, "permit");
344 purple_xmlnode_insert_data(child
, cur
->data
, -1);
347 for (cur
= purple_account_privacy_get_denied(account
); cur
; cur
= cur
->next
)
349 child
= purple_xmlnode_new_child(node
, "block");
350 purple_xmlnode_insert_data(child
, cur
->data
, -1);
356 static PurpleXmlNode
*
357 blist_to_xmlnode(void)
359 PurpleXmlNode
*node
, *child
, *grandchild
;
360 PurpleBlistNode
*gnode
;
362 const gchar
*localized_default
;
364 node
= purple_xmlnode_new("purple");
365 purple_xmlnode_set_attrib(node
, "version", "1.0");
368 child
= purple_xmlnode_new_child(node
, "blist");
370 localized_default
= localized_default_group_name
;
371 if (!purple_strequal(_("Buddies"), "Buddies"))
372 localized_default
= _("Buddies");
373 if (localized_default
!= NULL
) {
374 purple_xmlnode_set_attrib(child
,
375 "localized-default-group", localized_default
);
378 for (gnode
= purple_blist_get_default_root(); gnode
!= NULL
;
379 gnode
= gnode
->next
) {
380 if (purple_blist_node_is_transient(gnode
))
382 if (PURPLE_IS_GROUP(gnode
))
384 grandchild
= group_to_xmlnode(PURPLE_GROUP(gnode
));
385 purple_xmlnode_insert_child(child
, grandchild
);
389 /* Write privacy settings */
390 child
= purple_xmlnode_new_child(node
, "privacy");
391 for (cur
= purple_accounts_get_all(); cur
!= NULL
; cur
= cur
->next
)
393 grandchild
= accountprivacy_to_xmlnode(cur
->data
);
394 purple_xmlnode_insert_child(child
, grandchild
);
401 purple_blist_sync(void)
408 purple_debug_error("buddylist", "Attempted to save buddy list before it "
413 node
= blist_to_xmlnode();
414 data
= purple_xmlnode_to_formatted_str(node
, NULL
);
415 purple_util_write_data_to_config_file("blist.xml", data
, -1);
417 purple_xmlnode_free(node
);
421 save_cb(gpointer data
)
429 purple_blist_real_schedule_save(void)
432 save_timer
= g_timeout_add_seconds(5, save_cb
, NULL
);
436 purple_blist_real_save_account(PurpleBuddyList
*list
, PurpleAccount
*account
)
439 purple_blist_real_schedule_save();
441 if (account
!= NULL
) {
442 /* Save the buddies and privacy data for this account */
444 /* Save all buddies and privacy data */
450 purple_blist_real_save_node(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
452 purple_blist_real_schedule_save();
455 void purple_blist_schedule_save()
457 PurpleBuddyListClass
*klass
= NULL
;
459 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
461 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
463 /* Save everything */
464 if (klass
&& klass
->save_account
) {
465 klass
->save_account(purplebuddylist
, NULL
);
469 /*********************************************************************
470 * Reading from disk *
471 *********************************************************************/
474 parse_setting(PurpleBlistNode
*node
, PurpleXmlNode
*setting
)
476 const char *name
= purple_xmlnode_get_attrib(setting
, "name");
477 const char *type
= purple_xmlnode_get_attrib(setting
, "type");
478 char *value
= purple_xmlnode_get_data(setting
);
483 if (!type
|| purple_strequal(type
, "string"))
484 purple_blist_node_set_string(node
, name
, value
);
485 else if (purple_strequal(type
, "bool"))
486 purple_blist_node_set_bool(node
, name
, atoi(value
));
487 else if (purple_strequal(type
, "int"))
488 purple_blist_node_set_int(node
, name
, atoi(value
));
494 parse_buddy(PurpleGroup
*group
, PurpleContact
*contact
, PurpleXmlNode
*bnode
)
496 PurpleAccount
*account
;
498 char *name
= NULL
, *alias
= NULL
;
499 const char *acct_name
, *proto
;
502 acct_name
= purple_xmlnode_get_attrib(bnode
, "account");
503 proto
= purple_xmlnode_get_attrib(bnode
, "proto");
505 if (!acct_name
|| !proto
)
508 account
= purple_accounts_find(acct_name
, proto
);
513 if ((x
= purple_xmlnode_get_child(bnode
, "name")))
514 name
= purple_xmlnode_get_data(x
);
519 if ((x
= purple_xmlnode_get_child(bnode
, "alias")))
520 alias
= purple_xmlnode_get_data(x
);
522 buddy
= purple_buddy_new(account
, name
, alias
);
523 purple_blist_add_buddy(buddy
, contact
, group
,
524 _purple_blist_get_last_child((PurpleBlistNode
*)contact
));
526 for (x
= purple_xmlnode_get_child(bnode
, "setting"); x
; x
= purple_xmlnode_get_next_twin(x
)) {
527 parse_setting((PurpleBlistNode
*)buddy
, x
);
535 parse_contact(PurpleGroup
*group
, PurpleXmlNode
*cnode
)
537 PurpleContact
*contact
= purple_contact_new();
541 purple_blist_add_contact(contact
, group
,
542 _purple_blist_get_last_child((PurpleBlistNode
*)group
));
544 if ((alias
= purple_xmlnode_get_attrib(cnode
, "alias"))) {
545 purple_contact_set_alias(contact
, alias
);
548 for (x
= cnode
->child
; x
; x
= x
->next
) {
549 if (x
->type
!= PURPLE_XMLNODE_TYPE_TAG
)
551 if (purple_strequal(x
->name
, "buddy"))
552 parse_buddy(group
, contact
, x
);
553 else if (purple_strequal(x
->name
, "setting"))
554 parse_setting(PURPLE_BLIST_NODE(contact
), x
);
557 /* if the contact is empty, don't keep it around. it causes problems */
558 if (!PURPLE_BLIST_NODE(contact
)->child
)
559 purple_blist_remove_contact(contact
);
563 parse_chat(PurpleGroup
*group
, PurpleXmlNode
*cnode
)
566 PurpleAccount
*account
;
567 const char *acct_name
, *proto
;
570 GHashTable
*components
;
572 acct_name
= purple_xmlnode_get_attrib(cnode
, "account");
573 proto
= purple_xmlnode_get_attrib(cnode
, "proto");
575 if (!acct_name
|| !proto
)
578 account
= purple_accounts_find(acct_name
, proto
);
583 if ((x
= purple_xmlnode_get_child(cnode
, "alias")))
584 alias
= purple_xmlnode_get_data(x
);
586 components
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
588 for (x
= purple_xmlnode_get_child(cnode
, "component"); x
; x
= purple_xmlnode_get_next_twin(x
)) {
592 name
= purple_xmlnode_get_attrib(x
, "name");
593 value
= purple_xmlnode_get_data(x
);
594 g_hash_table_replace(components
, g_strdup(name
), value
);
597 chat
= purple_chat_new(account
, alias
, components
);
598 purple_blist_add_chat(chat
, group
,
599 _purple_blist_get_last_child((PurpleBlistNode
*)group
));
601 for (x
= purple_xmlnode_get_child(cnode
, "setting"); x
; x
= purple_xmlnode_get_next_twin(x
)) {
602 parse_setting((PurpleBlistNode
*)chat
, x
);
609 parse_group(PurpleXmlNode
*groupnode
)
611 const char *name
= purple_xmlnode_get_attrib(groupnode
, "name");
613 PurpleXmlNode
*cnode
;
615 group
= purple_group_new(name
);
616 purple_blist_add_group(group
, purple_blist_get_last_sibling(
617 purple_blist_get_default_root()));
619 for (cnode
= groupnode
->child
; cnode
; cnode
= cnode
->next
) {
620 if (cnode
->type
!= PURPLE_XMLNODE_TYPE_TAG
)
622 if (purple_strequal(cnode
->name
, "setting"))
623 parse_setting((PurpleBlistNode
*)group
, cnode
);
624 else if (purple_strequal(cnode
->name
, "contact") ||
625 purple_strequal(cnode
->name
, "person"))
626 parse_contact(group
, cnode
);
627 else if (purple_strequal(cnode
->name
, "chat"))
628 parse_chat(group
, cnode
);
635 PurpleXmlNode
*purple
, *blist
, *privacy
;
639 purple
= purple_util_read_xml_from_config_file("blist.xml", _("buddy list"));
644 blist
= purple_xmlnode_get_child(purple
, "blist");
646 PurpleXmlNode
*groupnode
;
648 localized_default_group_name
= g_strdup(
649 purple_xmlnode_get_attrib(blist
,
650 "localized-default-group"));
652 for (groupnode
= purple_xmlnode_get_child(blist
, "group"); groupnode
!= NULL
;
653 groupnode
= purple_xmlnode_get_next_twin(groupnode
)) {
654 parse_group(groupnode
);
657 g_free(localized_default_group_name
);
658 localized_default_group_name
= NULL
;
661 privacy
= purple_xmlnode_get_child(purple
, "privacy");
663 PurpleXmlNode
*anode
;
664 for (anode
= privacy
->child
; anode
; anode
= anode
->next
) {
666 PurpleAccount
*account
;
668 const char *acct_name
, *proto
, *mode
;
670 acct_name
= purple_xmlnode_get_attrib(anode
, "name");
671 proto
= purple_xmlnode_get_attrib(anode
, "proto");
672 mode
= purple_xmlnode_get_attrib(anode
, "mode");
674 if (!acct_name
|| !proto
|| !mode
)
677 account
= purple_accounts_find(acct_name
, proto
);
683 purple_account_set_privacy_type(account
, (imode
!= 0 ? imode
: PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL
));
685 for (x
= anode
->child
; x
; x
= x
->next
) {
687 if (x
->type
!= PURPLE_XMLNODE_TYPE_TAG
)
690 if (purple_strequal(x
->name
, "permit")) {
691 name
= purple_xmlnode_get_data(x
);
692 purple_account_privacy_permit_add(account
, name
, TRUE
);
694 } else if (purple_strequal(x
->name
, "block")) {
695 name
= purple_xmlnode_get_data(x
);
696 purple_account_privacy_deny_add(account
, name
, TRUE
);
703 purple_xmlnode_free(purple
);
705 /* This tells the buddy icon code to do its thing. */
706 _purple_buddy_icons_blist_loaded_cb();
709 /*****************************************************************************
710 * Public API functions *
711 *****************************************************************************/
714 purple_blist_set_ui(GType type
)
716 g_return_if_fail(g_type_is_a(type
, PURPLE_TYPE_BUDDY_LIST
) ||
717 type
== G_TYPE_INVALID
);
718 buddy_list_type
= type
;
722 purple_blist_boot(void)
725 PurpleBuddyList
*gbl
= g_object_new(buddy_list_type
, NULL
);
727 buddies_cache
= g_hash_table_new_full(g_direct_hash
, g_direct_equal
,
728 NULL
, (GDestroyNotify
)g_hash_table_destroy
);
730 groups_cache
= g_hash_table_new_full((GHashFunc
)g_str_hash
,
731 (GEqualFunc
)g_str_equal
,
732 (GDestroyNotify
)g_free
, NULL
);
734 for (account
= purple_accounts_get_all(); account
!= NULL
; account
= account
->next
)
736 purple_blist_buddies_cache_add_account(account
->data
);
739 purplebuddylist
= gbl
;
745 purple_blist_get_default(void)
747 return purplebuddylist
;
751 purple_blist_get_default_root(void)
753 if (purplebuddylist
) {
754 PurpleBuddyListPrivate
*priv
=
755 purple_buddy_list_get_instance_private(purplebuddylist
);
762 purple_blist_get_root(PurpleBuddyList
*list
)
764 PurpleBuddyListPrivate
*priv
= NULL
;
766 g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(list
), NULL
);
767 priv
= purple_buddy_list_get_instance_private(list
);
773 append_buddy(gpointer key
, gpointer value
, gpointer user_data
)
775 GSList
**list
= user_data
;
776 *list
= g_slist_prepend(*list
, value
);
780 purple_blist_get_buddies()
782 PurpleBuddyListPrivate
*priv
;
783 GSList
*buddies
= NULL
;
785 if (!purplebuddylist
)
788 priv
= purple_buddy_list_get_instance_private(purplebuddylist
);
789 g_hash_table_foreach(priv
->buddies
, append_buddy
, &buddies
);
793 void purple_blist_show()
795 PurpleBuddyListClass
*klass
= NULL
;
797 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
798 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
800 if (klass
&& klass
->show
) {
801 klass
->show(purplebuddylist
);
805 void purple_blist_set_visible(gboolean show
)
807 PurpleBuddyListClass
*klass
= NULL
;
809 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
810 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
812 if (klass
&& klass
->set_visible
) {
813 klass
->set_visible(purplebuddylist
, show
);
817 void purple_blist_update_buddies_cache(PurpleBuddy
*buddy
, const char *new_name
)
819 struct _purple_hbuddy
*hb
, *hb2
;
820 GHashTable
*account_buddies
;
821 PurpleAccount
*account
;
823 PurpleBuddyListPrivate
*priv
=
824 purple_buddy_list_get_instance_private(purplebuddylist
);
826 g_return_if_fail(PURPLE_IS_BUDDY(buddy
));
828 account
= purple_buddy_get_account(buddy
);
829 name
= (gchar
*)purple_buddy_get_name(buddy
);
831 hb
= g_new(struct _purple_hbuddy
, 1);
832 hb
->name
= (gchar
*)purple_normalize(account
, name
);
833 hb
->account
= account
;
834 hb
->group
= PURPLE_BLIST_NODE(buddy
)->parent
->parent
;
835 g_hash_table_remove(priv
->buddies
, hb
);
837 account_buddies
= g_hash_table_lookup(buddies_cache
, account
);
838 g_hash_table_remove(account_buddies
, hb
);
840 hb
->name
= g_strdup(purple_normalize(account
, new_name
));
841 g_hash_table_replace(priv
->buddies
, hb
, buddy
);
843 hb2
= g_new(struct _purple_hbuddy
, 1);
844 hb2
->name
= g_strdup(hb
->name
);
845 hb2
->account
= account
;
846 hb2
->group
= PURPLE_BLIST_NODE(buddy
)->parent
->parent
;
848 g_hash_table_replace(account_buddies
, hb2
, buddy
);
851 void purple_blist_update_groups_cache(PurpleGroup
*group
, const char *new_name
)
855 key
= purple_blist_fold_name(purple_group_get_name(group
));
856 g_hash_table_remove(groups_cache
, key
);
859 g_hash_table_insert(groups_cache
,
860 purple_blist_fold_name(new_name
), group
);
863 void purple_blist_add_chat(PurpleChat
*chat
, PurpleGroup
*group
, PurpleBlistNode
*node
)
865 PurpleBlistNode
*cnode
= PURPLE_BLIST_NODE(chat
);
866 PurpleBuddyListClass
*klass
= NULL
;
867 PurpleCountingNode
*group_counter
;
869 g_return_if_fail(PURPLE_IS_CHAT(chat
));
870 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
871 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
875 group
= purple_group_new(_("Chats"));
877 /* Add group to blist if isn't already on it. Fixes #2752. */
878 if (!purple_blist_find_group(purple_group_get_name(group
))) {
879 purple_blist_add_group(
881 purple_blist_get_last_sibling(
882 purple_blist_get_default_root()));
885 group
= PURPLE_GROUP(node
->parent
);
888 /* if we're moving to overtop of ourselves, do nothing */
893 /* This chat was already in the list and is
896 group_counter
= PURPLE_COUNTING_NODE(cnode
->parent
);
897 purple_counting_node_change_total_size(group_counter
, -1);
898 if (purple_account_is_connected(purple_chat_get_account(chat
))) {
899 purple_counting_node_change_online_count(group_counter
, -1);
900 purple_counting_node_change_current_size(group_counter
, -1);
903 cnode
->next
->prev
= cnode
->prev
;
905 cnode
->prev
->next
= cnode
->next
;
906 if (cnode
->parent
->child
== cnode
)
907 cnode
->parent
->child
= cnode
->next
;
909 if (klass
&& klass
->remove
) {
910 klass
->remove(purplebuddylist
, cnode
);
912 /* ops->remove() cleaned up the cnode's ui_data, so we need to
914 if (klass
&& klass
->new_node
) {
915 klass
->new_node(purplebuddylist
, cnode
);
921 node
->next
->prev
= cnode
;
922 cnode
->next
= node
->next
;
924 cnode
->parent
= node
->parent
;
926 group_counter
= PURPLE_COUNTING_NODE(node
->parent
);
927 purple_counting_node_change_total_size(group_counter
, +1);
928 if (purple_account_is_connected(purple_chat_get_account(chat
))) {
929 purple_counting_node_change_online_count(group_counter
, +1);
930 purple_counting_node_change_current_size(group_counter
, +1);
933 if (((PurpleBlistNode
*)group
)->child
)
934 ((PurpleBlistNode
*)group
)->child
->prev
= cnode
;
935 cnode
->next
= ((PurpleBlistNode
*)group
)->child
;
937 ((PurpleBlistNode
*)group
)->child
= cnode
;
938 cnode
->parent
= PURPLE_BLIST_NODE(group
);
939 group_counter
= PURPLE_COUNTING_NODE(group
);
940 purple_counting_node_change_total_size(group_counter
, +1);
941 if (purple_account_is_connected(purple_chat_get_account(chat
))) {
942 purple_counting_node_change_online_count(group_counter
, +1);
943 purple_counting_node_change_current_size(group_counter
, +1);
948 if (klass
->save_node
) {
949 klass
->save_node(purplebuddylist
, cnode
);
952 klass
->update(purplebuddylist
,
953 PURPLE_BLIST_NODE(cnode
));
957 purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
961 void purple_blist_add_buddy(PurpleBuddy
*buddy
, PurpleContact
*contact
, PurpleGroup
*group
, PurpleBlistNode
*node
)
963 PurpleBuddyListClass
*klass
= NULL
;
964 PurpleBuddyListPrivate
*priv
= NULL
;
965 PurpleBlistNode
*cnode
, *bnode
;
966 PurpleCountingNode
*contact_counter
, *group_counter
;
969 PurpleAccount
*account
;
970 struct _purple_hbuddy
*hb
, *hb2
;
971 GHashTable
*account_buddies
;
973 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
974 g_return_if_fail(PURPLE_IS_BUDDY(buddy
));
976 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
977 priv
= purple_buddy_list_get_instance_private(purplebuddylist
);
978 bnode
= PURPLE_BLIST_NODE(buddy
);
979 account
= purple_buddy_get_account(buddy
);
981 /* if we're moving to overtop of ourselves, do nothing */
982 if (bnode
== node
|| (!node
&& bnode
->parent
&&
983 contact
&& bnode
->parent
== (PurpleBlistNode
*)contact
984 && bnode
== bnode
->parent
->child
))
987 if (node
&& PURPLE_IS_BUDDY(node
)) {
988 c
= (PurpleContact
*)node
->parent
;
989 g
= (PurpleGroup
*)node
->parent
->parent
;
990 } else if (contact
) {
992 g
= PURPLE_GROUP(PURPLE_BLIST_NODE(c
)->parent
);
996 g
= purple_blist_get_default_group();
997 /* Add group to blist if isn't already on it. Fixes #2752. */
998 if (!purple_blist_find_group(purple_group_get_name(g
))) {
999 purple_blist_add_group(
1000 g
, purple_blist_get_last_sibling(priv
->root
));
1002 c
= purple_contact_new();
1003 purple_blist_add_contact(c
, g
,
1004 _purple_blist_get_last_child((PurpleBlistNode
*)g
));
1007 cnode
= PURPLE_BLIST_NODE(c
);
1009 if (bnode
->parent
) {
1010 contact_counter
= PURPLE_COUNTING_NODE(bnode
->parent
);
1011 group_counter
= PURPLE_COUNTING_NODE(bnode
->parent
->parent
);
1013 if (PURPLE_BUDDY_IS_ONLINE(buddy
)) {
1014 purple_counting_node_change_online_count(contact_counter
, -1);
1015 if (purple_counting_node_get_online_count(contact_counter
) == 0)
1016 purple_counting_node_change_online_count(group_counter
, -1);
1018 if (purple_account_is_connected(account
)) {
1019 purple_counting_node_change_current_size(contact_counter
, -1);
1020 if (purple_counting_node_get_current_size(contact_counter
) == 0)
1021 purple_counting_node_change_current_size(group_counter
, -1);
1023 purple_counting_node_change_total_size(contact_counter
, -1);
1024 /* the group totalsize will be taken care of by remove_contact below */
1026 if (bnode
->parent
->parent
!= (PurpleBlistNode
*)g
) {
1027 purple_signal_emit(purple_blist_get_handle(), "buddy-removed-from-group", buddy
);
1028 purple_serv_move_buddy(buddy
, (PurpleGroup
*)bnode
->parent
->parent
, g
);
1032 bnode
->next
->prev
= bnode
->prev
;
1034 bnode
->prev
->next
= bnode
->next
;
1035 if (bnode
->parent
->child
== bnode
)
1036 bnode
->parent
->child
= bnode
->next
;
1038 if (klass
&& klass
->remove
) {
1039 klass
->remove(purplebuddylist
, bnode
);
1042 if (bnode
->parent
->parent
!= (PurpleBlistNode
*)g
) {
1043 struct _purple_hbuddy hb
;
1044 hb
.name
= (gchar
*)purple_normalize(account
,
1045 purple_buddy_get_name(buddy
));
1046 hb
.account
= account
;
1047 hb
.group
= bnode
->parent
->parent
;
1048 g_hash_table_remove(priv
->buddies
, &hb
);
1050 account_buddies
= g_hash_table_lookup(buddies_cache
, account
);
1051 g_hash_table_remove(account_buddies
, &hb
);
1054 if (!bnode
->parent
->child
) {
1055 purple_blist_remove_contact((PurpleContact
*)bnode
->parent
);
1057 purple_contact_invalidate_priority_buddy((PurpleContact
*)bnode
->parent
);
1059 if (klass
&& klass
->update
) {
1060 klass
->update(purplebuddylist
, bnode
->parent
);
1065 if (node
&& PURPLE_IS_BUDDY(node
)) {
1067 node
->next
->prev
= bnode
;
1068 bnode
->next
= node
->next
;
1070 bnode
->parent
= node
->parent
;
1074 cnode
->child
->prev
= bnode
;
1076 bnode
->next
= cnode
->child
;
1077 cnode
->child
= bnode
;
1078 bnode
->parent
= cnode
;
1081 contact_counter
= PURPLE_COUNTING_NODE(bnode
->parent
);
1082 group_counter
= PURPLE_COUNTING_NODE(bnode
->parent
->parent
);
1084 if (PURPLE_BUDDY_IS_ONLINE(buddy
)) {
1085 purple_counting_node_change_online_count(contact_counter
, +1);
1086 if (purple_counting_node_get_online_count(contact_counter
) == 1)
1087 purple_counting_node_change_online_count(group_counter
, +1);
1089 if (purple_account_is_connected(account
)) {
1090 purple_counting_node_change_current_size(contact_counter
, +1);
1091 if (purple_counting_node_get_current_size(contact_counter
) == 1)
1092 purple_counting_node_change_current_size(group_counter
, +1);
1094 purple_counting_node_change_total_size(contact_counter
, +1);
1096 hb
= g_new(struct _purple_hbuddy
, 1);
1097 hb
->name
= g_strdup(purple_normalize(account
, purple_buddy_get_name(buddy
)));
1098 hb
->account
= account
;
1099 hb
->group
= PURPLE_BLIST_NODE(buddy
)->parent
->parent
;
1101 g_hash_table_replace(priv
->buddies
, hb
, buddy
);
1103 account_buddies
= g_hash_table_lookup(buddies_cache
, account
);
1105 hb2
= g_new(struct _purple_hbuddy
, 1);
1106 hb2
->name
= g_strdup(hb
->name
);
1107 hb2
->account
= account
;
1108 hb2
->group
= ((PurpleBlistNode
*)buddy
)->parent
->parent
;
1110 g_hash_table_replace(account_buddies
, hb2
, buddy
);
1112 purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy
));
1115 if (klass
->save_node
) {
1116 klass
->save_node(purplebuddylist
,
1117 (PurpleBlistNode
*)buddy
);
1119 if (klass
->update
) {
1120 klass
->update(purplebuddylist
,
1121 PURPLE_BLIST_NODE(buddy
));
1125 /* Signal that the buddy has been added */
1126 purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
1127 PURPLE_BLIST_NODE(buddy
));
1130 void purple_blist_add_contact(PurpleContact
*contact
, PurpleGroup
*group
, PurpleBlistNode
*node
)
1132 PurpleBuddyListClass
*klass
= NULL
;
1133 PurpleBuddyListPrivate
*priv
= NULL
;
1135 PurpleBlistNode
*gnode
, *cnode
, *bnode
;
1136 PurpleCountingNode
*contact_counter
, *group_counter
;
1138 g_return_if_fail(PURPLE_IS_CONTACT(contact
));
1139 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
1141 if (PURPLE_BLIST_NODE(contact
) == node
)
1144 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
1145 priv
= purple_buddy_list_get_instance_private(purplebuddylist
);
1147 if (node
&& (PURPLE_IS_CONTACT(node
) ||
1148 PURPLE_IS_CHAT(node
)))
1149 g
= PURPLE_GROUP(node
->parent
);
1153 g
= purple_blist_get_default_group();
1155 gnode
= (PurpleBlistNode
*)g
;
1156 cnode
= (PurpleBlistNode
*)contact
;
1158 if (cnode
->parent
) {
1159 if (cnode
->parent
->child
== cnode
)
1160 cnode
->parent
->child
= cnode
->next
;
1162 cnode
->prev
->next
= cnode
->next
;
1164 cnode
->next
->prev
= cnode
->prev
;
1166 if (cnode
->parent
!= gnode
) {
1167 bnode
= cnode
->child
;
1169 PurpleBlistNode
*next_bnode
= bnode
->next
;
1170 PurpleBuddy
*b
= PURPLE_BUDDY(bnode
);
1171 PurpleAccount
*account
= purple_buddy_get_account(b
);
1172 GHashTable
*account_buddies
;
1174 struct _purple_hbuddy
*hb
, *hb2
;
1176 hb
= g_new(struct _purple_hbuddy
, 1);
1177 hb
->name
= g_strdup(purple_normalize(account
, purple_buddy_get_name(b
)));
1178 hb
->account
= account
;
1179 hb
->group
= cnode
->parent
;
1181 g_hash_table_remove(priv
->buddies
, hb
);
1183 account_buddies
= g_hash_table_lookup(buddies_cache
, account
);
1184 g_hash_table_remove(account_buddies
, hb
);
1186 if (!purple_blist_find_buddy_in_group(account
, purple_buddy_get_name(b
), g
)) {
1188 g_hash_table_replace(priv
->buddies
, hb
, b
);
1190 hb2
= g_new(struct _purple_hbuddy
, 1);
1191 hb2
->name
= g_strdup(hb
->name
);
1192 hb2
->account
= account
;
1195 g_hash_table_replace(account_buddies
, hb2
, b
);
1197 if (purple_account_get_connection(account
))
1198 purple_serv_move_buddy(b
, (PurpleGroup
*)cnode
->parent
, g
);
1200 gboolean empty_contact
= FALSE
;
1202 /* this buddy already exists in the group, so we're
1203 * gonna delete it instead */
1206 if (purple_account_get_connection(account
))
1207 purple_account_remove_buddy(account
, b
, PURPLE_GROUP(cnode
->parent
));
1209 if (!cnode
->child
->next
)
1210 empty_contact
= TRUE
;
1211 purple_blist_remove_buddy(b
);
1213 /* in purple_blist_remove_buddy(), if the last buddy in a
1214 * contact is removed, the contact is cleaned up and
1215 * g_free'd, so we mustn't try to reference bnode->next */
1223 contact_counter
= PURPLE_COUNTING_NODE(contact
);
1224 group_counter
= PURPLE_COUNTING_NODE(cnode
->parent
);
1226 if (purple_counting_node_get_online_count(contact_counter
) > 0)
1227 purple_counting_node_change_online_count(group_counter
, -1);
1228 if (purple_counting_node_get_current_size(contact_counter
) > 0)
1229 purple_counting_node_change_current_size(group_counter
, -1);
1230 purple_counting_node_change_total_size(group_counter
, -1);
1232 if (klass
&& klass
->remove
) {
1233 klass
->remove(purplebuddylist
, cnode
);
1236 if (klass
&& klass
->remove_node
) {
1237 klass
->remove_node(purplebuddylist
, cnode
);
1241 if (node
&& (PURPLE_IS_CONTACT(node
) ||
1242 PURPLE_IS_CHAT(node
))) {
1244 node
->next
->prev
= cnode
;
1245 cnode
->next
= node
->next
;
1247 cnode
->parent
= node
->parent
;
1251 gnode
->child
->prev
= cnode
;
1253 cnode
->next
= gnode
->child
;
1254 gnode
->child
= cnode
;
1255 cnode
->parent
= gnode
;
1258 contact_counter
= PURPLE_COUNTING_NODE(contact
);
1259 group_counter
= PURPLE_COUNTING_NODE(g
);
1261 if (purple_counting_node_get_online_count(contact_counter
) > 0)
1262 purple_counting_node_change_online_count(group_counter
, +1);
1263 if (purple_counting_node_get_current_size(contact_counter
) > 0)
1264 purple_counting_node_change_current_size(group_counter
, +1);
1265 purple_counting_node_change_total_size(group_counter
, +1);
1267 if (klass
&& klass
->save_node
) {
1269 klass
->save_node(purplebuddylist
, cnode
);
1271 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
1272 klass
->save_node(purplebuddylist
, bnode
);
1276 if (klass
&& klass
->update
) {
1278 klass
->update(purplebuddylist
, cnode
);
1281 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
1282 klass
->update(purplebuddylist
, bnode
);
1287 void purple_blist_add_group(PurpleGroup
*group
, PurpleBlistNode
*node
)
1289 PurpleBuddyListClass
*klass
= NULL
;
1290 PurpleBuddyListPrivate
*priv
= NULL
;
1291 PurpleBlistNode
*gnode
= (PurpleBlistNode
*)group
;
1294 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
1295 g_return_if_fail(PURPLE_IS_GROUP(group
));
1297 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
1298 priv
= purple_buddy_list_get_instance_private(purplebuddylist
);
1300 /* if we're moving to overtop of ourselves, do nothing */
1301 if (gnode
== node
) {
1309 if (purple_blist_find_group(purple_group_get_name(group
))) {
1310 /* This is just being moved */
1312 if (klass
&& klass
->remove
) {
1313 klass
->remove(purplebuddylist
,
1314 (PurpleBlistNode
*)group
);
1317 if (gnode
== priv
->root
) {
1318 priv
->root
= gnode
->next
;
1321 gnode
->prev
->next
= gnode
->next
;
1323 gnode
->next
->prev
= gnode
->prev
;
1325 key
= purple_blist_fold_name(purple_group_get_name(group
));
1326 g_hash_table_insert(groups_cache
, key
, group
);
1329 if (node
&& PURPLE_IS_GROUP(node
)) {
1330 gnode
->next
= node
->next
;
1333 node
->next
->prev
= gnode
;
1337 priv
->root
->prev
= gnode
;
1339 gnode
->next
= priv
->root
;
1344 if (klass
&& klass
->save_node
) {
1345 klass
->save_node(purplebuddylist
, gnode
);
1346 for (node
= gnode
->child
; node
; node
= node
->next
) {
1347 klass
->save_node(purplebuddylist
, node
);
1351 if (klass
&& klass
->update
) {
1352 klass
->update(purplebuddylist
, gnode
);
1353 for (node
= gnode
->child
; node
; node
= node
->next
) {
1354 klass
->update(purplebuddylist
, node
);
1358 purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
1362 void purple_blist_remove_contact(PurpleContact
*contact
)
1364 PurpleBuddyListClass
*klass
= NULL
;
1365 PurpleBlistNode
*node
, *gnode
;
1368 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
1369 g_return_if_fail(PURPLE_IS_CONTACT(contact
));
1371 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
1372 node
= (PurpleBlistNode
*)contact
;
1373 gnode
= node
->parent
;
1374 group
= PURPLE_GROUP(gnode
);
1378 * If this contact has children then remove them. When the last
1379 * buddy is removed from the contact, the contact is automatically
1382 while (node
->child
->next
) {
1383 purple_blist_remove_buddy((PurpleBuddy
*)node
->child
);
1386 * Remove the last buddy and trigger the deletion of the contact.
1387 * It would probably be cleaner if contact-deletion was done after
1388 * a timeout? Or if it had to be done manually, like below?
1390 purple_blist_remove_buddy((PurpleBuddy
*)node
->child
);
1392 /* Remove the node from its parent */
1393 if (gnode
->child
== node
)
1394 gnode
->child
= node
->next
;
1396 node
->prev
->next
= node
->next
;
1398 node
->next
->prev
= node
->prev
;
1399 purple_counting_node_change_total_size(PURPLE_COUNTING_NODE(group
), -1);
1402 if (klass
&& klass
->remove
) {
1403 klass
->remove(purplebuddylist
, node
);
1406 if (klass
&& klass
->remove_node
) {
1407 klass
->remove_node(purplebuddylist
, node
);
1410 purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
1411 PURPLE_BLIST_NODE(contact
));
1413 /* Delete the node */
1414 g_object_unref(contact
);
1418 void purple_blist_remove_buddy(PurpleBuddy
*buddy
)
1420 PurpleBuddyListClass
*klass
= NULL
;
1421 PurpleBuddyListPrivate
*priv
= NULL
;
1422 PurpleBlistNode
*node
, *cnode
, *gnode
;
1423 PurpleCountingNode
*contact_counter
, *group_counter
;
1424 PurpleContact
*contact
;
1426 struct _purple_hbuddy hb
;
1427 GHashTable
*account_buddies
;
1428 PurpleAccount
*account
;
1430 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
1431 g_return_if_fail(PURPLE_IS_BUDDY(buddy
));
1433 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
1434 priv
= purple_buddy_list_get_instance_private(purplebuddylist
);
1435 account
= purple_buddy_get_account(buddy
);
1436 node
= PURPLE_BLIST_NODE(buddy
);
1437 cnode
= node
->parent
;
1438 gnode
= (cnode
!= NULL
) ? cnode
->parent
: NULL
;
1439 contact
= (PurpleContact
*)cnode
;
1440 group
= (PurpleGroup
*)gnode
;
1442 /* Remove the node from its parent */
1444 node
->prev
->next
= node
->next
;
1446 node
->next
->prev
= node
->prev
;
1447 if ((cnode
!= NULL
) && (cnode
->child
== node
))
1448 cnode
->child
= node
->next
;
1450 /* Adjust size counts */
1451 if (contact
!= NULL
) {
1452 contact_counter
= PURPLE_COUNTING_NODE(contact
);
1453 group_counter
= PURPLE_COUNTING_NODE(group
);
1455 if (PURPLE_BUDDY_IS_ONLINE(buddy
)) {
1456 purple_counting_node_change_online_count(contact_counter
, -1);
1457 if (purple_counting_node_get_online_count(contact_counter
) == 0)
1458 purple_counting_node_change_online_count(group_counter
, -1);
1460 if (purple_account_is_connected(account
)) {
1461 purple_counting_node_change_current_size(contact_counter
, -1);
1462 if (purple_counting_node_get_current_size(contact_counter
) == 0)
1463 purple_counting_node_change_current_size(group_counter
, -1);
1465 purple_counting_node_change_total_size(contact_counter
, -1);
1467 /* Re-sort the contact */
1468 if (cnode
->child
&& purple_contact_get_priority_buddy(contact
) == buddy
) {
1469 purple_contact_invalidate_priority_buddy(contact
);
1471 if (klass
&& klass
->update
) {
1472 klass
->update(purplebuddylist
, cnode
);
1477 /* Remove this buddy from the buddies hash table */
1478 hb
.name
= (gchar
*)purple_normalize(account
, purple_buddy_get_name(buddy
));
1479 hb
.account
= account
;
1481 g_hash_table_remove(priv
->buddies
, &hb
);
1483 account_buddies
= g_hash_table_lookup(buddies_cache
, account
);
1484 g_hash_table_remove(account_buddies
, &hb
);
1487 if (klass
&& klass
->remove
) {
1488 klass
->remove(purplebuddylist
, node
);
1491 if (klass
&& klass
->remove_node
) {
1492 klass
->remove_node(purplebuddylist
, node
);
1495 /* Remove this buddy's pounces */
1496 purple_pounce_destroy_all_by_buddy(buddy
);
1498 /* Signal that the buddy has been removed before freeing the memory for it */
1499 purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
1500 PURPLE_BLIST_NODE(buddy
));
1502 g_object_unref(buddy
);
1504 /* If the contact is empty then remove it */
1505 if ((contact
!= NULL
) && !cnode
->child
)
1506 purple_blist_remove_contact(contact
);
1509 void purple_blist_remove_chat(PurpleChat
*chat
)
1511 PurpleBuddyListClass
*klass
= NULL
;
1512 PurpleBlistNode
*node
, *gnode
;
1514 PurpleCountingNode
*group_counter
;
1516 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
1517 g_return_if_fail(PURPLE_IS_CHAT(chat
));
1519 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
1520 node
= (PurpleBlistNode
*)chat
;
1521 gnode
= node
->parent
;
1522 group
= (PurpleGroup
*)gnode
;
1526 /* Remove the node from its parent */
1527 if (gnode
->child
== node
)
1528 gnode
->child
= node
->next
;
1530 node
->prev
->next
= node
->next
;
1532 node
->next
->prev
= node
->prev
;
1534 /* Adjust size counts */
1535 group_counter
= PURPLE_COUNTING_NODE(group
);
1536 if (purple_account_is_connected(purple_chat_get_account(chat
))) {
1537 purple_counting_node_change_online_count(group_counter
, -1);
1538 purple_counting_node_change_current_size(group_counter
, -1);
1540 purple_counting_node_change_total_size(group_counter
, -1);
1544 if (klass
&& klass
->remove
) {
1545 klass
->remove(purplebuddylist
, node
);
1548 if (klass
&& klass
->remove_node
) {
1549 klass
->remove_node(purplebuddylist
, node
);
1552 purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
1553 PURPLE_BLIST_NODE(chat
));
1555 /* Delete the node */
1556 g_object_unref(chat
);
1559 void purple_blist_remove_group(PurpleGroup
*group
)
1561 PurpleBuddyListClass
*klass
= NULL
;
1562 PurpleBuddyListPrivate
*priv
= NULL
;
1563 PurpleBlistNode
*node
;
1567 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
1568 g_return_if_fail(PURPLE_IS_GROUP(group
));
1570 if (group
== purple_blist_get_default_group())
1571 purple_debug_warning("buddylist", "cannot remove default group");
1573 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
1574 priv
= purple_buddy_list_get_instance_private(purplebuddylist
);
1575 node
= (PurpleBlistNode
*)group
;
1577 /* Make sure the group is empty */
1581 /* Remove the node from its parent */
1582 if (priv
->root
== node
) {
1583 priv
->root
= node
->next
;
1586 node
->prev
->next
= node
->next
;
1588 node
->next
->prev
= node
->prev
;
1590 key
= purple_blist_fold_name(purple_group_get_name(group
));
1591 g_hash_table_remove(groups_cache
, key
);
1595 if (klass
&& klass
->remove
) {
1596 klass
->remove(purplebuddylist
, node
);
1599 if (klass
&& klass
->remove_node
) {
1600 klass
->remove_node(purplebuddylist
, node
);
1603 purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
1604 PURPLE_BLIST_NODE(group
));
1606 /* Remove the group from all accounts that are online */
1607 for (l
= purple_connections_get_all(); l
!= NULL
; l
= l
->next
)
1609 PurpleConnection
*gc
= (PurpleConnection
*)l
->data
;
1611 if (purple_connection_get_state(gc
) == PURPLE_CONNECTION_CONNECTED
)
1612 purple_account_remove_group(purple_connection_get_account(gc
), group
);
1615 /* Delete the node */
1616 g_object_unref(group
);
1619 PurpleBuddy
*purple_blist_find_buddy(PurpleAccount
*account
, const char *name
)
1621 PurpleBuddyListPrivate
*priv
=
1622 purple_buddy_list_get_instance_private(purplebuddylist
);
1624 struct _purple_hbuddy hb
;
1625 PurpleBlistNode
*group
;
1627 g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
), NULL
);
1628 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account
), NULL
);
1629 g_return_val_if_fail((name
!= NULL
) && (*name
!= '\0'), NULL
);
1631 hb
.account
= account
;
1632 hb
.name
= (gchar
*)purple_normalize(account
, name
);
1634 for (group
= priv
->root
; group
; group
= group
->next
) {
1639 if ((buddy
= g_hash_table_lookup(priv
->buddies
, &hb
))) {
1647 PurpleBuddy
*purple_blist_find_buddy_in_group(PurpleAccount
*account
, const char *name
,
1650 PurpleBuddyListPrivate
*priv
=
1651 purple_buddy_list_get_instance_private(purplebuddylist
);
1652 struct _purple_hbuddy hb
;
1654 g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
), NULL
);
1655 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account
), NULL
);
1656 g_return_val_if_fail((name
!= NULL
) && (*name
!= '\0'), NULL
);
1658 hb
.name
= (gchar
*)purple_normalize(account
, name
);
1659 hb
.account
= account
;
1660 hb
.group
= (PurpleBlistNode
*)group
;
1662 return g_hash_table_lookup(priv
->buddies
, &hb
);
1665 static void find_acct_buddies(gpointer key
, gpointer value
, gpointer data
)
1667 PurpleBuddy
*buddy
= value
;
1668 GSList
**list
= data
;
1670 *list
= g_slist_prepend(*list
, buddy
);
1673 GSList
*purple_blist_find_buddies(PurpleAccount
*account
, const char *name
)
1675 PurpleBuddyListPrivate
*priv
=
1676 purple_buddy_list_get_instance_private(purplebuddylist
);
1678 PurpleBlistNode
*node
;
1681 g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
), NULL
);
1682 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account
), NULL
);
1684 if ((name
!= NULL
) && (*name
!= '\0')) {
1685 struct _purple_hbuddy hb
;
1687 hb
.name
= (gchar
*)purple_normalize(account
, name
);
1688 hb
.account
= account
;
1690 for (node
= priv
->root
; node
!= NULL
; node
= node
->next
) {
1695 if ((buddy
= g_hash_table_lookup(priv
->buddies
,
1697 ret
= g_slist_prepend(ret
, buddy
);
1700 GSList
*list
= NULL
;
1701 GHashTable
*buddies
= g_hash_table_lookup(buddies_cache
, account
);
1702 g_hash_table_foreach(buddies
, find_acct_buddies
, &list
);
1709 PurpleGroup
*purple_blist_find_group(const char *name
)
1714 g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
), NULL
);
1716 if (name
== NULL
|| name
[0] == '\0')
1717 name
= PURPLE_BLIST_DEFAULT_GROUP_NAME
;
1718 if (purple_strequal(name
, "Buddies"))
1719 name
= PURPLE_BLIST_DEFAULT_GROUP_NAME
;
1720 if (purple_strequal(name
, localized_default_group_name
))
1721 name
= PURPLE_BLIST_DEFAULT_GROUP_NAME
;
1723 key
= purple_blist_fold_name(name
);
1724 group
= g_hash_table_lookup(groups_cache
, key
);
1731 purple_blist_get_default_group(void)
1735 group
= purple_blist_find_group(PURPLE_BLIST_DEFAULT_GROUP_NAME
);
1737 group
= purple_group_new(PURPLE_BLIST_DEFAULT_GROUP_NAME
);
1738 purple_blist_add_group(group
, NULL
);
1745 purple_blist_find_chat(PurpleAccount
*account
, const char *name
)
1749 PurpleProtocol
*protocol
= NULL
;
1750 PurpleProtocolChatEntry
*pce
;
1751 PurpleBlistNode
*node
, *group
;
1755 g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
), NULL
);
1756 g_return_val_if_fail((name
!= NULL
) && (*name
!= '\0'), NULL
);
1758 if (!purple_account_is_connected(account
))
1761 protocol
= purple_protocols_find(purple_account_get_protocol_id(account
));
1763 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol
, CLIENT
, find_blist_chat
))
1764 return purple_protocol_client_iface_find_blist_chat(protocol
, account
, name
);
1766 normname
= g_strdup(purple_normalize(account
, name
));
1767 for (group
= purple_blist_get_default_root(); group
!= NULL
;
1768 group
= group
->next
) {
1769 for (node
= group
->child
; node
!= NULL
; node
= node
->next
) {
1770 if (PURPLE_IS_CHAT(node
)) {
1772 chat
= (PurpleChat
*)node
;
1774 if (account
!= purple_chat_get_account(chat
))
1777 parts
= purple_protocol_chat_iface_info(protocol
,
1778 purple_account_get_connection(purple_chat_get_account(chat
)));
1781 chat_name
= g_hash_table_lookup(purple_chat_get_components(chat
),
1783 g_list_free_full(parts
, g_free
);
1785 if (purple_chat_get_account(chat
) == account
&& chat_name
!= NULL
&&
1786 purple_strequal(purple_normalize(account
, chat_name
), normname
)) {
1798 void purple_blist_add_account(PurpleAccount
*account
)
1800 PurpleBuddyListClass
*klass
= NULL
;
1801 PurpleBlistNode
*gnode
, *cnode
, *bnode
;
1802 PurpleCountingNode
*contact_counter
, *group_counter
;
1804 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
1806 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
1807 if (!klass
|| !klass
->update
) {
1811 for (gnode
= purple_blist_get_default_root(); gnode
;
1812 gnode
= gnode
->next
) {
1813 if (!PURPLE_IS_GROUP(gnode
))
1815 for (cnode
= gnode
->child
; cnode
; cnode
= cnode
->next
) {
1816 if (PURPLE_IS_CONTACT(cnode
)) {
1817 gboolean recompute
= FALSE
;
1818 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
1819 if (PURPLE_IS_BUDDY(bnode
) &&
1820 purple_buddy_get_account(PURPLE_BUDDY(bnode
)) == account
) {
1822 contact_counter
= PURPLE_COUNTING_NODE(cnode
);
1823 group_counter
= PURPLE_COUNTING_NODE(gnode
);
1824 purple_counting_node_change_current_size(contact_counter
, +1);
1825 if (purple_counting_node_get_current_size(contact_counter
) == 1)
1826 purple_counting_node_change_current_size(group_counter
, +1);
1833 purple_blist_node_get_bool(
1834 cnode
, "show_offline")) {
1835 purple_contact_invalidate_priority_buddy(
1836 (PurpleContact
*)cnode
);
1837 klass
->update(purplebuddylist
,
1840 } else if (PURPLE_IS_CHAT(cnode
) &&
1841 purple_chat_get_account(PURPLE_CHAT(cnode
)) == account
) {
1842 group_counter
= PURPLE_COUNTING_NODE(gnode
);
1843 purple_counting_node_change_online_count(group_counter
, +1);
1844 purple_counting_node_change_current_size(group_counter
, +1);
1845 klass
->update(purplebuddylist
, cnode
);
1848 klass
->update(purplebuddylist
, gnode
);
1852 void purple_blist_remove_account(PurpleAccount
*account
)
1854 PurpleBuddyListClass
*klass
= NULL
;
1855 PurpleBlistNode
*gnode
, *cnode
, *bnode
;
1856 PurpleCountingNode
*contact_counter
, *group_counter
;
1859 PurpleContact
*contact
;
1861 GList
*list
= NULL
, *iter
= NULL
;
1863 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
1864 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
1866 for (gnode
= purple_blist_get_default_root(); gnode
;
1867 gnode
= gnode
->next
) {
1868 if (!PURPLE_IS_GROUP(gnode
))
1871 group
= (PurpleGroup
*)gnode
;
1873 for (cnode
= gnode
->child
; cnode
; cnode
= cnode
->next
) {
1874 if (PURPLE_IS_CONTACT(cnode
)) {
1875 gboolean recompute
= FALSE
;
1876 contact
= (PurpleContact
*)cnode
;
1878 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
1879 if (!PURPLE_IS_BUDDY(bnode
))
1882 buddy
= (PurpleBuddy
*)bnode
;
1883 if (account
== purple_buddy_get_account(buddy
)) {
1884 PurplePresence
*presence
;
1886 presence
= purple_buddy_get_presence(buddy
);
1887 contact_counter
= PURPLE_COUNTING_NODE(contact
);
1888 group_counter
= PURPLE_COUNTING_NODE(group
);
1890 if(purple_presence_is_online(presence
)) {
1891 purple_counting_node_change_online_count(contact_counter
, -1);
1892 if (purple_counting_node_get_online_count(contact_counter
) == 0)
1893 purple_counting_node_change_online_count(group_counter
, -1);
1895 purple_blist_node_set_int(PURPLE_BLIST_NODE(buddy
),
1896 "last_seen", time(NULL
));
1899 purple_counting_node_change_current_size(contact_counter
, -1);
1900 if (purple_counting_node_get_current_size(contact_counter
) == 0)
1901 purple_counting_node_change_current_size(group_counter
, -1);
1903 if (!g_list_find(list
, presence
))
1904 list
= g_list_prepend(list
, presence
);
1906 if (purple_contact_get_priority_buddy(contact
) == buddy
)
1907 purple_contact_invalidate_priority_buddy(contact
);
1911 if (klass
&& klass
->remove
) {
1919 purple_contact_invalidate_priority_buddy(contact
);
1921 if (klass
&& klass
->update
) {
1922 klass
->update(purplebuddylist
,
1926 } else if (PURPLE_IS_CHAT(cnode
)) {
1927 chat
= PURPLE_CHAT(cnode
);
1929 if(purple_chat_get_account(chat
) == account
) {
1930 group_counter
= PURPLE_COUNTING_NODE(group
);
1931 purple_counting_node_change_current_size(group_counter
, -1);
1932 purple_counting_node_change_online_count(group_counter
, -1);
1934 if (klass
&& klass
->remove
) {
1935 klass
->remove(purplebuddylist
,
1943 for (iter
= list
; iter
; iter
= iter
->next
)
1945 purple_presence_set_status_active(iter
->data
, "offline", TRUE
);
1951 purple_blist_walk(PurpleBlistWalkFunc group_func
,
1952 PurpleBlistWalkFunc chat_func
,
1953 PurpleBlistWalkFunc meta_contact_func
,
1954 PurpleBlistWalkFunc contact_func
,
1957 PurpleBlistNode
*group
= NULL
, *meta_contact
= NULL
, *contact
= NULL
;
1959 for (group
= purple_blist_get_default_root(); group
!= NULL
;
1960 group
= group
->next
) {
1961 if(group_func
!= NULL
) {
1962 group_func(group
, data
);
1965 for(meta_contact
= group
->child
; meta_contact
!= NULL
; meta_contact
= meta_contact
->next
) {
1966 if(PURPLE_IS_CONTACT(meta_contact
)) {
1967 if(meta_contact_func
!= NULL
) {
1968 meta_contact_func(meta_contact
, data
);
1971 if(contact_func
!= NULL
) {
1972 for(contact
= meta_contact
->child
; contact
!= NULL
; contact
= contact
->next
) {
1973 contact_func(contact
, data
);
1977 if(PURPLE_IS_CHAT(meta_contact
) && chat_func
!= NULL
) {
1978 chat_func(meta_contact
, data
);
1987 purple_blist_request_add_buddy(PurpleAccount
*account
, const char *username
,
1988 const char *group
, const char *alias
)
1990 PurpleBuddyListClass
*klass
= NULL
;
1992 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
1994 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
1995 if (klass
!= NULL
&& klass
->request_add_buddy
!= NULL
) {
1996 klass
->request_add_buddy(purplebuddylist
, account
, username
,
2002 purple_blist_request_add_chat(PurpleAccount
*account
, PurpleGroup
*group
,
2003 const char *alias
, const char *name
)
2005 PurpleBuddyListClass
*klass
= NULL
;
2007 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
2009 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
2010 if (klass
!= NULL
&& klass
->request_add_chat
!= NULL
) {
2011 klass
->request_add_chat(purplebuddylist
, account
, group
, alias
,
2017 purple_blist_request_add_group(void)
2019 PurpleBuddyListClass
*klass
= NULL
;
2021 g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist
));
2023 klass
= PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist
);
2024 if (klass
!= NULL
&& klass
->request_add_group
!= NULL
) {
2025 klass
->request_add_group(purplebuddylist
);
2030 purple_blist_new_node(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
2032 PurpleBuddyListClass
*klass
= NULL
;
2034 g_return_if_fail(PURPLE_IS_BUDDY_LIST(list
));
2036 klass
= PURPLE_BUDDY_LIST_GET_CLASS(list
);
2037 if (klass
&& klass
->new_node
) {
2038 klass
->new_node(list
, node
);
2043 purple_blist_update_node(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
2045 PurpleBuddyListClass
*klass
= NULL
;
2047 g_return_if_fail(PURPLE_IS_BUDDY_LIST(list
));
2049 klass
= PURPLE_BUDDY_LIST_GET_CLASS(list
);
2050 if (klass
&& klass
->update
) {
2051 klass
->update(list
, node
);
2056 purple_blist_save_node(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
2058 PurpleBuddyListClass
*klass
= NULL
;
2060 g_return_if_fail(PURPLE_IS_BUDDY_LIST(list
));
2062 klass
= PURPLE_BUDDY_LIST_GET_CLASS(list
);
2063 if (klass
&& klass
->save_node
) {
2064 klass
->save_node(list
, node
);
2069 purple_blist_save_account(PurpleBuddyList
*list
, PurpleAccount
*account
)
2071 PurpleBuddyListClass
*klass
= NULL
;
2073 /* XXX: There's a chicken and egg problem with the accounts api, where
2074 * it'll call this function before purple_blist_init is called, this will
2075 * cause the following g_return_if_fail to fail, and muck up the logs. We
2076 * need to find a better fix for this, but this gets rid of it for now.
2078 if(G_UNLIKELY(list
== NULL
&& purplebuddylist
== NULL
)) {
2082 g_return_if_fail(PURPLE_IS_BUDDY_LIST(list
));
2084 klass
= PURPLE_BUDDY_LIST_GET_CLASS(list
);
2085 if (klass
&& klass
->save_account
) {
2086 klass
->save_account(list
, account
);
2091 _purple_blist_get_localized_default_group_name(void)
2093 return localized_default_group_name
;
2097 purple_blist_get_handle(void)
2105 purple_blist_init(void)
2107 void *handle
= purple_blist_get_handle();
2109 /* Set a default, which can't be done as a static initializer. */
2110 buddy_list_type
= PURPLE_TYPE_BUDDY_LIST
;
2112 purple_signal_register(handle
, "buddy-status-changed",
2113 purple_marshal_VOID__POINTER_POINTER_POINTER
,
2114 G_TYPE_NONE
, 3, PURPLE_TYPE_BUDDY
, PURPLE_TYPE_STATUS
,
2115 PURPLE_TYPE_STATUS
);
2116 purple_signal_register(handle
, "buddy-privacy-changed",
2117 purple_marshal_VOID__POINTER
, G_TYPE_NONE
,
2118 1, PURPLE_TYPE_BUDDY
);
2120 purple_signal_register(handle
, "buddy-idle-changed",
2121 purple_marshal_VOID__POINTER_INT_INT
, G_TYPE_NONE
,
2122 3, PURPLE_TYPE_BUDDY
, G_TYPE_INT
, G_TYPE_INT
);
2124 purple_signal_register(handle
, "buddy-signed-on",
2125 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2128 purple_signal_register(handle
, "buddy-signed-off",
2129 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2132 purple_signal_register(handle
, "buddy-got-login-time",
2133 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2136 purple_signal_register(handle
, "blist-node-added",
2137 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2138 PURPLE_TYPE_BLIST_NODE
);
2140 purple_signal_register(handle
, "blist-node-removed",
2141 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2142 PURPLE_TYPE_BLIST_NODE
);
2144 purple_signal_register(handle
, "buddy-removed-from-group",
2145 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2148 purple_signal_register(handle
, "buddy-icon-changed",
2149 purple_marshal_VOID__POINTER
, G_TYPE_NONE
, 1,
2152 purple_signal_register(handle
, "update-idle", purple_marshal_VOID
,
2155 purple_signal_register(handle
, "blist-node-extended-menu",
2156 purple_marshal_VOID__POINTER_POINTER
, G_TYPE_NONE
, 2,
2157 PURPLE_TYPE_BLIST_NODE
,
2158 G_TYPE_POINTER
); /* (GList **) */
2160 purple_signal_register(handle
, "blist-node-aliased",
2161 purple_marshal_VOID__POINTER_POINTER
, G_TYPE_NONE
, 2,
2162 PURPLE_TYPE_BLIST_NODE
, G_TYPE_STRING
);
2164 purple_signal_register(handle
, "buddy-caps-changed",
2165 purple_marshal_VOID__POINTER_INT_INT
, G_TYPE_NONE
,
2166 3, PURPLE_TYPE_BUDDY
, G_TYPE_INT
, G_TYPE_INT
);
2168 purple_signal_connect(purple_accounts_get_handle(), "account-created",
2170 PURPLE_CALLBACK(purple_blist_buddies_cache_add_account
),
2173 purple_signal_connect(purple_accounts_get_handle(), "account-destroying",
2175 PURPLE_CALLBACK(purple_blist_buddies_cache_remove_account
),
2180 blist_node_destroy(PurpleBuddyListClass
*klass
, PurpleBuddyList
*list
,
2181 PurpleBlistNode
*node
)
2183 PurpleBlistNode
*child
, *next_child
;
2185 child
= node
->child
;
2187 next_child
= child
->next
;
2188 blist_node_destroy(klass
, list
, child
);
2192 /* Allow the UI to free data */
2193 node
->parent
= NULL
;
2197 if (klass
&& klass
->remove
) {
2198 klass
->remove(list
, node
);
2201 g_object_unref(node
);
2205 purple_blist_uninit(void)
2207 /* This happens if we quit before purple_set_blist is called. */
2208 if (purplebuddylist
== NULL
)
2211 if (save_timer
!= 0) {
2212 g_source_remove(save_timer
);
2214 purple_blist_sync();
2217 purple_debug(PURPLE_DEBUG_INFO
, "buddylist", "Destroying\n");
2219 g_hash_table_destroy(buddies_cache
);
2220 g_hash_table_destroy(groups_cache
);
2222 buddies_cache
= NULL
;
2223 groups_cache
= NULL
;
2225 g_clear_object(&purplebuddylist
);
2227 g_free(localized_default_group_name
);
2228 localized_default_group_name
= NULL
;
2230 purple_signals_disconnect_by_handle(purple_blist_get_handle());
2231 purple_signals_unregister_by_instance(purple_blist_get_handle());
2234 /**************************************************************************
2236 **************************************************************************/
2238 /* GObject initialization function */
2240 purple_buddy_list_init(PurpleBuddyList
*blist
)
2242 PurpleBuddyListPrivate
*priv
=
2243 purple_buddy_list_get_instance_private(blist
);
2245 priv
->buddies
= g_hash_table_new_full(
2246 (GHashFunc
)_purple_blist_hbuddy_hash
,
2247 (GEqualFunc
)_purple_blist_hbuddy_equal
,
2248 (GDestroyNotify
)_purple_blist_hbuddy_free_key
, NULL
);
2251 /* GObject finalize function */
2253 purple_buddy_list_finalize(GObject
*object
)
2255 PurpleBuddyList
*list
= PURPLE_BUDDY_LIST(object
);
2256 PurpleBuddyListClass
*klass
= PURPLE_BUDDY_LIST_GET_CLASS(list
);
2257 PurpleBuddyListPrivate
*priv
=
2258 purple_buddy_list_get_instance_private(list
);
2259 PurpleBlistNode
*node
, *next_node
;
2261 g_hash_table_destroy(priv
->buddies
);
2265 next_node
= node
->next
;
2266 blist_node_destroy(klass
, list
, node
);
2271 G_OBJECT_CLASS(purple_buddy_list_parent_class
)->finalize(object
);
2274 /* Class initializer function */
2275 static void purple_buddy_list_class_init(PurpleBuddyListClass
*klass
)
2277 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
2279 obj_class
->finalize
= purple_buddy_list_finalize
;
2281 klass
->save_node
= purple_blist_real_save_node
;
2282 klass
->remove_node
= purple_blist_real_save_node
;
2283 klass
->save_account
= purple_blist_real_save_account
;