4 * Finch 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
26 #include NCURSES_HEADER
30 #include <buddylist.h>
35 #include <savedstatuses.h>
43 #include "gntcolors.h"
44 #include "gntcombobox.h"
51 #include "gntmenuitem.h"
52 #include "gntmenuitemcheck.h"
53 #include "gntmenuutil.h"
54 #include "gntpounce.h"
58 #include "gntwindow.h"
62 #include "gntstatus.h"
65 #define PREF_ROOT "/finch/blist"
66 #define TYPING_TIMEOUT_S 4
68 #define SHOW_EMPTY_GROUP_TIMEOUT 60
70 struct _FinchBuddyList
{
71 PurpleBuddyList parent
;
77 PurpleBlistNode
*tnode
; /* Who is the tooltip being displayed for? */
78 GList
*tagged
; /* A list of tagged blistnodes */
81 PurpleBlistNode
*cnode
;
83 /* XXX: I am KISSing */
84 GntWidget
*status
; /* Dropdown with the statuses */
85 GntWidget
*statustext
; /* Status message */
89 /* These are the menuitems that get regenerated */
90 GntMenuItem
*accounts
;
92 GntMenuItem
*grouping
;
94 /* When a new group is manually added, it is empty, but we still want to show it
95 * for a while (SHOW_EMPTY_GROUP_TIMEOUT seconds) even if 'show empty groups' is
99 guint new_group_timeout
;
101 FinchBlistManager
*manager
;
106 gpointer row
; /* the row in the GntTree */
107 guint signed_timer
; /* used when 'recently' signed on/off */
112 STATUS_PRIMITIVE
= 0,
113 STATUS_SAVED_POPULAR
,
123 PurpleStatusPrimitive prim
;
124 PurpleSavedStatus
*saved
;
128 static FinchBuddyList
*ggblist
;
130 static void add_buddy(PurpleBuddy
*buddy
, FinchBuddyList
*ggblist
);
131 static void add_contact(PurpleContact
*contact
, FinchBuddyList
*ggblist
);
132 static void add_group(PurpleGroup
*group
, FinchBuddyList
*ggblist
);
133 static void add_chat(PurpleChat
*chat
, FinchBuddyList
*ggblist
);
134 static void add_node(PurpleBlistNode
*node
, FinchBuddyList
*ggblist
);
135 static void node_update(PurpleBuddyList
*list
, PurpleBlistNode
*node
);
136 static void draw_tooltip(FinchBuddyList
*ggblist
);
137 static void tooltip_for_buddy(PurpleBuddy
*buddy
, GString
*str
, gboolean full
);
138 static gboolean
remove_typing_cb(gpointer null
);
139 static void remove_peripherals(FinchBuddyList
*ggblist
);
140 static const char * get_display_name(PurpleBlistNode
*node
);
141 static void savedstatus_changed(PurpleSavedStatus
*now
, PurpleSavedStatus
*old
);
142 static void blist_show(PurpleBuddyList
*list
);
143 static void update_node_display(PurpleBlistNode
*buddy
,
144 FinchBuddyList
*ggblist
);
145 static void update_buddy_display(PurpleBuddy
*buddy
, FinchBuddyList
*ggblist
);
146 static gboolean
account_autojoin_cb(PurpleConnection
*pc
, gpointer null
);
147 static void finch_request_add_buddy(PurpleBuddyList
*list
,
148 PurpleAccount
*account
,
149 const char *username
, const char *grp
,
151 static void menu_group_set_cb(GntMenuItem
*item
, gpointer null
);
154 static int blist_node_compare_position(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
155 static int blist_node_compare_text(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
156 static int blist_node_compare_status(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
157 static int blist_node_compare_log(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
159 static int color_available
;
160 static int color_away
;
161 static int color_offline
;
162 static int color_idle
;
165 * Buddy List Manager functions.
168 static gboolean
default_can_add_node(PurpleBlistNode
*node
)
170 gboolean offline
= purple_prefs_get_bool(PREF_ROOT
"/showoffline");
172 if (PURPLE_IS_BUDDY(node
)) {
173 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
174 FinchBlistNode
*fnode
= purple_blist_node_get_ui_data(node
);
175 if (!purple_buddy_get_contact(buddy
))
176 return FALSE
; /* When a new buddy is added and show-offline is set */
177 if (PURPLE_BUDDY_IS_ONLINE(buddy
))
178 return TRUE
; /* The buddy is online */
179 if (!purple_account_is_connected(purple_buddy_get_account(buddy
)))
180 return FALSE
; /* The account is disconnected. Do not show */
182 return TRUE
; /* We want to see offline buddies too */
183 if (fnode
&& fnode
->signed_timer
)
184 return TRUE
; /* Show if the buddy just signed off */
185 if (purple_blist_node_get_bool(node
, "show_offline"))
187 } else if (PURPLE_IS_CONTACT(node
)) {
189 for (nd
= purple_blist_node_get_first_child(node
);
190 nd
; nd
= purple_blist_node_get_sibling_next(nd
)) {
191 if (default_can_add_node(nd
))
194 } else if (PURPLE_IS_CHAT(node
)) {
195 PurpleChat
*chat
= (PurpleChat
*)node
;
196 if (purple_account_is_connected(purple_chat_get_account(chat
)))
197 return TRUE
; /* Show whenever the account is online */
198 } else if (PURPLE_IS_GROUP(node
)) {
200 gboolean empty
= purple_prefs_get_bool(PREF_ROOT
"/emptygroups");
202 return TRUE
; /* If we want to see empty groups, we can show any group */
204 for (nd
= purple_blist_node_get_first_child(node
);
205 nd
; nd
= purple_blist_node_get_sibling_next(nd
)) {
206 if (default_can_add_node(nd
))
210 if (ggblist
&& ggblist
->new_group
&& g_list_find(ggblist
->new_group
, node
))
217 static gpointer
default_find_parent(PurpleBlistNode
*node
)
221 if (PURPLE_IS_BUDDY(node
) || PURPLE_IS_CONTACT(node
) || PURPLE_IS_CHAT(node
))
222 ret
= purple_blist_node_get_parent(node
);
225 add_node(ret
, ggblist
);
230 static gboolean
default_create_tooltip(gpointer selected_row
, GString
**body
, char **tool_title
)
233 PurpleBlistNode
*node
= selected_row
;
237 str
= g_string_new("");
239 if (PURPLE_IS_CONTACT(node
)) {
240 PurpleBuddy
*pr
= purple_contact_get_priority_buddy((PurpleContact
*)node
);
241 gboolean offline
= !PURPLE_BUDDY_IS_ONLINE(pr
);
242 gboolean showoffline
= purple_prefs_get_bool(PREF_ROOT
"/showoffline");
243 const char *name
= purple_buddy_get_name(pr
);
245 title
= g_strdup(name
);
246 tooltip_for_buddy(pr
, str
, TRUE
);
247 for (node
= purple_blist_node_get_first_child(node
); node
; node
= purple_blist_node_get_sibling_next(node
)) {
248 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
250 int value
= purple_blist_node_get_int(node
, "last_seen");
251 if (value
> lastseen
)
254 if (node
== (PurpleBlistNode
*)pr
)
256 if (!purple_account_is_connected(purple_buddy_get_account(buddy
)))
258 if (!showoffline
&& !PURPLE_BUDDY_IS_ONLINE(buddy
))
260 str
= g_string_append(str
, "\n----------\n");
261 tooltip_for_buddy(buddy
, str
, FALSE
);
263 } else if (PURPLE_IS_BUDDY(node
)) {
264 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
265 tooltip_for_buddy(buddy
, str
, TRUE
);
266 title
= g_strdup(purple_buddy_get_name(buddy
));
267 if (!PURPLE_BUDDY_IS_ONLINE((PurpleBuddy
*)node
))
268 lastseen
= purple_blist_node_get_int(node
, "last_seen");
269 } else if (PURPLE_IS_GROUP(node
)) {
270 PurpleGroup
*group
= (PurpleGroup
*)node
;
272 g_string_append_printf(str
, _("Online: %d\nTotal: %d"),
273 purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group
)),
274 purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(group
)));
276 title
= g_strdup(purple_group_get_name(group
));
277 } else if (PURPLE_IS_CHAT(node
)) {
278 PurpleChat
*chat
= (PurpleChat
*)node
;
279 PurpleAccount
*account
= purple_chat_get_account(chat
);
281 g_string_append_printf(str
, _("Account: %s (%s)"),
282 purple_account_get_username(account
),
283 purple_account_get_protocol_name(account
));
285 title
= g_strdup(purple_chat_get_name(chat
));
287 g_string_free(str
, TRUE
);
292 char *tmp
= purple_str_seconds_to_string(time(NULL
) - lastseen
);
293 g_string_append_printf(str
, _("\nLast Seen: %s ago"), tmp
);
305 g_string_free(str
, TRUE
);
310 static FinchBlistManager default_manager
=
316 default_can_add_node
,
318 default_create_tooltip
,
319 {NULL
, NULL
, NULL
, NULL
}
321 static GList
*managers
;
323 static FinchBlistNode
*
324 create_finch_blist_node(PurpleBlistNode
*node
, gpointer row
)
326 FinchBlistNode
*fnode
= purple_blist_node_get_ui_data(node
);
328 fnode
= g_new0(FinchBlistNode
, 1);
329 fnode
->signed_timer
= 0;
330 purple_blist_node_set_ui_data(node
, fnode
);
337 reset_blist_node_ui_data(PurpleBlistNode
*node
)
339 FinchBlistNode
*fnode
= purple_blist_node_get_ui_data(node
);
342 if (fnode
->signed_timer
)
343 g_source_remove(fnode
->signed_timer
);
345 purple_blist_node_set_ui_data(node
, NULL
);
349 get_display_color(PurpleBlistNode
*node
)
354 if (PURPLE_IS_CONTACT(node
))
355 node
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
)));
356 if (!PURPLE_IS_BUDDY(node
))
359 buddy
= (PurpleBuddy
*)node
;
360 if (purple_presence_is_idle(purple_buddy_get_presence(buddy
))) {
362 } else if (purple_presence_is_available(purple_buddy_get_presence(buddy
))) {
363 color
= color_available
;
364 } else if (purple_presence_is_online(purple_buddy_get_presence(buddy
)) &&
365 !purple_presence_is_available(purple_buddy_get_presence(buddy
))) {
367 } else if (!purple_presence_is_online(purple_buddy_get_presence(buddy
))) {
368 color
= color_offline
;
374 static GntTextFormatFlags
375 get_blist_node_flag(FinchBuddyList
*ggblist
, PurpleBlistNode
*node
)
377 GntTextFormatFlags flag
= 0;
378 FinchBlistNode
*fnode
= purple_blist_node_get_ui_data(node
);
380 if (ggblist
->tagged
&& g_list_find(ggblist
->tagged
, node
))
381 flag
|= GNT_TEXT_FLAG_BOLD
;
383 if (fnode
&& fnode
->signed_timer
)
384 flag
|= GNT_TEXT_FLAG_BLINK
;
385 else if (PURPLE_IS_CONTACT(node
)) {
386 node
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
)));
387 fnode
= purple_blist_node_get_ui_data(node
);
388 if (fnode
&& fnode
->signed_timer
)
389 flag
|= GNT_TEXT_FLAG_BLINK
;
396 blist_update_row_flags(FinchBuddyList
*ggblist
, PurpleBlistNode
*node
)
398 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), node
,
399 get_blist_node_flag(ggblist
, node
));
400 gnt_tree_set_row_color(GNT_TREE(ggblist
->tree
), node
, get_display_color(node
));
404 new_node(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
409 add_node(PurpleBlistNode
*node
, FinchBuddyList
*ggblist
)
411 if (purple_blist_node_get_ui_data(node
))
414 if (!ggblist
->manager
->can_add_node(node
))
417 if (PURPLE_IS_BUDDY(node
))
418 add_buddy((PurpleBuddy
*)node
, ggblist
);
419 else if (PURPLE_IS_CONTACT(node
))
420 add_contact((PurpleContact
*)node
, ggblist
);
421 else if (PURPLE_IS_GROUP(node
))
422 add_group((PurpleGroup
*)node
, ggblist
);
423 else if (PURPLE_IS_CHAT(node
))
424 add_chat((PurpleChat
*)node
, ggblist
);
426 draw_tooltip(ggblist
);
429 void finch_blist_manager_add_node(PurpleBlistNode
*node
)
431 add_node(node
, ggblist
);
435 remove_tooltip(FinchBuddyList
*ggblist
)
437 gnt_widget_destroy(ggblist
->tooltip
);
438 ggblist
->tooltip
= NULL
;
439 ggblist
->tnode
= NULL
;
443 node_remove(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
445 FinchBuddyList
*ggblist
= FINCH_BUDDY_LIST(list
);
446 PurpleBlistNode
*parent
;
448 if (ggblist
== NULL
|| purple_blist_node_get_ui_data(node
) == NULL
)
451 if (PURPLE_IS_GROUP(node
) && ggblist
->new_group
) {
452 ggblist
->new_group
= g_list_remove(ggblist
->new_group
, node
);
455 gnt_tree_remove(GNT_TREE(ggblist
->tree
), node
);
456 reset_blist_node_ui_data(node
);
458 ggblist
->tagged
= g_list_remove(ggblist
->tagged
, node
);
460 parent
= purple_blist_node_get_parent(node
);
461 for (node
= purple_blist_node_get_first_child(node
); node
;
462 node
= purple_blist_node_get_sibling_next(node
))
463 node_remove(list
, node
);
466 if (!ggblist
->manager
->can_add_node(parent
))
467 node_remove(list
, parent
);
469 node_update(list
, parent
);
472 draw_tooltip(ggblist
);
476 node_update(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
478 FinchBuddyList
*ggblist
;
480 g_return_if_fail(FINCH_IS_BUDDY_LIST(list
));
481 /* It really looks like this should never happen ... but it does.
482 This will at least emit a warning to the log when it
483 happens, so maybe someone will figure it out. */
484 g_return_if_fail(node
!= NULL
);
486 ggblist
= FINCH_BUDDY_LIST(list
);
487 if (ggblist
->window
== NULL
)
490 if (purple_blist_node_get_ui_data(node
)!= NULL
) {
491 gnt_tree_change_text(GNT_TREE(ggblist
->tree
), node
,
492 0, get_display_name(node
));
493 gnt_tree_sort_row(GNT_TREE(ggblist
->tree
), node
);
494 blist_update_row_flags(ggblist
, node
);
495 if (gnt_tree_get_parent_key(GNT_TREE(ggblist
->tree
), node
) !=
496 ggblist
->manager
->find_parent(node
))
497 node_remove(list
, node
);
500 if (PURPLE_IS_BUDDY(node
)) {
501 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
502 add_node((PurpleBlistNode
*)buddy
, FINCH_BUDDY_LIST(list
));
503 node_update(list
, purple_blist_node_get_parent(node
));
504 } else if (PURPLE_IS_CHAT(node
)) {
505 add_node(node
, FINCH_BUDDY_LIST(list
));
506 } else if (PURPLE_IS_CONTACT(node
)) {
507 if (purple_blist_node_get_ui_data(node
)== NULL
) {
508 /* The core seems to expect the UI to add the buddies. */
509 for (node
= purple_blist_node_get_first_child(node
); node
; node
= purple_blist_node_get_sibling_next(node
))
510 add_node(node
, FINCH_BUDDY_LIST(list
));
512 } else if (PURPLE_IS_GROUP(node
)) {
513 if (!ggblist
->manager
->can_add_node(node
))
514 node_remove(list
, node
);
516 add_node(node
, FINCH_BUDDY_LIST(list
));
518 if (ggblist
->tnode
== node
) {
519 draw_tooltip(ggblist
);
524 remove_new_empty_group(gpointer data
)
526 PurpleBuddyList
*list
;
527 FinchBuddyList
*ggblist
;
529 list
= purple_blist_get_default();
530 g_return_val_if_fail(list
, FALSE
);
531 ggblist
= FINCH_BUDDY_LIST(list
);
533 ggblist
->new_group_timeout
= 0;
534 while (ggblist
->new_group
) {
535 PurpleBlistNode
*group
= ggblist
->new_group
->data
;
536 ggblist
->new_group
= g_list_delete_link(ggblist
->new_group
, ggblist
->new_group
);
537 node_update(list
, group
);
544 add_buddy_cb(void *data
, PurpleRequestFields
*allfields
)
546 const char *username
= purple_request_fields_get_string(allfields
, "screenname");
547 const char *alias
= purple_request_fields_get_string(allfields
, "alias");
548 const char *group
= purple_request_fields_get_string(allfields
, "group");
549 const char *invite
= purple_request_fields_get_string(allfields
, "invite");
550 PurpleAccount
*account
= purple_request_fields_get_account(allfields
, "account");
551 const char *error
= NULL
;
556 error
= _("You must provide a username for the buddy.");
558 error
= _("You must provide a group.");
560 error
= _("You must select an account.");
561 else if (!purple_account_is_connected(account
))
562 error
= _("The selected account is not online.");
566 finch_request_add_buddy(purple_blist_get_default(), account
,
567 username
, group
, alias
);
568 purple_notify_error(NULL
, _("Error"), _("Error adding buddy"),
569 error
, purple_request_cpar_from_account(account
));
573 grp
= purple_blist_find_group(group
);
576 grp
= purple_group_new(group
);
577 purple_blist_add_group(grp
, NULL
);
580 /* XXX: Ask to merge if there's already a buddy with the same alias in the same group (#4553) */
582 if ((buddy
= purple_blist_find_buddy_in_group(account
, username
, grp
)) == NULL
)
584 buddy
= purple_buddy_new(account
, username
, alias
);
585 purple_blist_add_buddy(buddy
, NULL
, grp
, NULL
);
588 purple_account_add_buddy(account
, buddy
, invite
);
592 finch_request_add_buddy(PurpleBuddyList
*list
, PurpleAccount
*account
,
593 const char *username
, const char *grp
,
596 PurpleRequestFields
*fields
= purple_request_fields_new();
597 PurpleRequestFieldGroup
*group
= purple_request_field_group_new(NULL
);
598 PurpleRequestField
*field
;
600 purple_request_fields_add_group(fields
, group
);
602 field
= purple_request_field_string_new("screenname", _("Username"), username
, FALSE
);
603 purple_request_field_group_add_field(group
, field
);
605 field
= purple_request_field_string_new("alias", _("Alias (optional)"), alias
, FALSE
);
606 purple_request_field_group_add_field(group
, field
);
608 field
= purple_request_field_string_new("invite", _("Invite message (optional)"), NULL
, FALSE
);
609 purple_request_field_group_add_field(group
, field
);
611 field
= purple_request_field_string_new("group", _("Add in group"), grp
, FALSE
);
612 purple_request_field_group_add_field(group
, field
);
613 purple_request_field_set_type_hint(field
, "group");
615 field
= purple_request_field_account_new("account", _("Account"), NULL
);
616 purple_request_field_account_set_show_all(field
, FALSE
);
618 purple_request_field_account_set_value(field
, account
);
619 purple_request_field_group_add_field(group
, field
);
621 purple_request_fields(NULL
, _("Add Buddy"), NULL
, _("Please enter buddy information."),
623 _("Add"), G_CALLBACK(add_buddy_cb
),
625 purple_request_cpar_from_account(account
),
630 join_chat(PurpleChat
*chat
)
632 PurpleAccount
*account
= purple_chat_get_account(chat
);
634 PurpleChatConversation
*conv
;
636 name
= purple_chat_get_name_only(chat
);
637 conv
= purple_conversations_find_chat_with_account(name
, account
);
639 if (!conv
|| purple_chat_conversation_has_left(conv
)) {
640 purple_serv_join_chat(purple_account_get_connection(account
),
641 purple_chat_get_components(chat
));
643 purple_conversation_present(PURPLE_CONVERSATION(conv
));
648 add_chat_cb(void *data
, PurpleRequestFields
*allfields
)
650 PurpleAccount
*account
;
651 const char *alias
, *name
, *group
;
654 GHashTable
*hash
= NULL
;
655 PurpleConnection
*gc
;
657 PurpleProtocol
*protocol
;
659 account
= purple_request_fields_get_account(allfields
, "account");
660 name
= purple_request_fields_get_string(allfields
, "name");
661 alias
= purple_request_fields_get_string(allfields
, "alias");
662 group
= purple_request_fields_get_string(allfields
, "group");
663 autojoin
= purple_request_fields_get_bool(allfields
, "autojoin");
665 if (!purple_account_is_connected(account
) || !name
|| !*name
)
668 if (!group
|| !*group
)
671 gc
= purple_account_get_connection(account
);
672 protocol
= purple_connection_get_protocol(gc
);
673 hash
= purple_protocol_chat_iface_info_defaults(protocol
, gc
, name
);
675 chat
= purple_chat_new(account
, name
, hash
);
678 if ((grp
= purple_blist_find_group(group
)) == NULL
) {
679 grp
= purple_group_new(group
);
680 purple_blist_add_group(grp
, NULL
);
682 purple_blist_add_chat(chat
, grp
, NULL
);
683 purple_chat_set_alias(chat
, alias
);
684 purple_blist_node_set_bool((PurpleBlistNode
*)chat
, "gnt-autojoin", autojoin
);
692 finch_request_add_chat(PurpleBuddyList
*list
, PurpleAccount
*account
,
693 PurpleGroup
*grp
, const char *alias
, const char *name
)
695 PurpleRequestFields
*fields
= purple_request_fields_new();
696 PurpleRequestFieldGroup
*group
= purple_request_field_group_new(NULL
);
697 PurpleRequestField
*field
;
699 purple_request_fields_add_group(fields
, group
);
701 field
= purple_request_field_account_new("account", _("Account"), NULL
);
702 purple_request_field_account_set_show_all(field
, FALSE
);
704 purple_request_field_account_set_value(field
, account
);
705 purple_request_field_group_add_field(group
, field
);
707 field
= purple_request_field_string_new("name", _("Name"), name
, FALSE
);
708 purple_request_field_group_add_field(group
, field
);
710 field
= purple_request_field_string_new("alias", _("Alias"), alias
, FALSE
);
711 purple_request_field_group_add_field(group
, field
);
713 field
= purple_request_field_string_new("group", _("Group"), grp
? purple_group_get_name(grp
) : NULL
, FALSE
);
714 purple_request_field_group_add_field(group
, field
);
715 purple_request_field_set_type_hint(field
, "group");
717 field
= purple_request_field_bool_new("autojoin", _("Auto-join"), FALSE
);
718 purple_request_field_group_add_field(group
, field
);
720 purple_request_fields(NULL
, _("Add Chat"), NULL
,
721 _("You can edit more information from the context menu later."),
722 fields
, _("Add"), G_CALLBACK(add_chat_cb
), _("Cancel"), NULL
,
727 add_group_cb(FinchBuddyList
*ggblist
, const char *group
)
731 if (!group
|| !*group
) {
732 purple_notify_error(NULL
, _("Error"), _("Error adding group"),
733 _("You must give a name for the group to add."), NULL
);
734 g_object_unref(ggblist
);
738 grp
= purple_blist_find_group(group
);
740 grp
= purple_group_new(group
);
741 purple_blist_add_group(grp
, NULL
);
744 /* Treat the group as a new group even if it had existed before. This should
745 * make things easier to add buddies to empty groups (new or old) without having
746 * to turn on 'show empty groups' setting */
747 ggblist
->new_group
= g_list_prepend(ggblist
->new_group
, grp
);
748 if (ggblist
->new_group_timeout
)
749 g_source_remove(ggblist
->new_group_timeout
);
750 ggblist
->new_group_timeout
= g_timeout_add_seconds(SHOW_EMPTY_GROUP_TIMEOUT
,
751 remove_new_empty_group
, NULL
);
753 /* Select the group */
755 FinchBlistNode
*fnode
= purple_blist_node_get_ui_data((PurpleBlistNode
*)grp
);
757 add_node((PurpleBlistNode
*)grp
, ggblist
);
758 gnt_tree_set_selected(GNT_TREE(ggblist
->tree
), grp
);
761 g_object_unref(ggblist
);
765 finch_request_add_group(PurpleBuddyList
*list
)
767 purple_request_input(NULL
, _("Add Group"), NULL
,
768 _("Enter the name of the group"), NULL
, FALSE
,
769 FALSE
, NULL
, _("Add"), G_CALLBACK(add_group_cb
),
770 _("Cancel"), G_CALLBACK(g_object_unref
), NULL
,
775 finch_blist_get_handle(void)
783 add_group(PurpleGroup
*group
, FinchBuddyList
*ggblist
)
786 PurpleBlistNode
*node
= (PurpleBlistNode
*)group
;
787 if (purple_blist_node_get_ui_data(node
))
789 parent
= ggblist
->manager
->find_parent((PurpleBlistNode
*)group
);
790 create_finch_blist_node(node
, gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), group
,
791 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), get_display_name(node
)),
793 gnt_tree_set_expanded(GNT_TREE(ggblist
->tree
), node
,
794 !purple_blist_node_get_bool(node
, "collapsed"));
798 get_display_name(PurpleBlistNode
*node
)
800 static char text
[2096];
801 char status
[8] = " ";
802 const char *name
= NULL
;
804 if (PURPLE_IS_CONTACT(node
))
805 node
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
))); /* XXX: this can return NULL?! */
810 if (PURPLE_IS_BUDDY(node
))
812 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
813 PurpleStatusPrimitive prim
;
814 PurplePresence
*presence
;
816 gboolean ascii
= gnt_ascii_only();
818 presence
= purple_buddy_get_presence(buddy
);
819 if (purple_presence_is_status_primitive_active(presence
, PURPLE_STATUS_MOBILE
))
820 strncpy(status
, ascii
? ":" : "☎", sizeof(status
) - 1);
822 now
= purple_presence_get_active_status(presence
);
824 prim
= purple_status_type_get_primitive(purple_status_get_status_type(now
));
827 case PURPLE_STATUS_OFFLINE
:
828 strncpy(status
, ascii
? "x" : "⊗", sizeof(status
) - 1);
830 case PURPLE_STATUS_AVAILABLE
:
831 strncpy(status
, ascii
? "o" : "â—¯", sizeof(status
) - 1);
834 strncpy(status
, ascii
? "." : "⊖", sizeof(status
) - 1);
838 name
= purple_buddy_get_alias(buddy
);
840 else if (PURPLE_IS_CHAT(node
))
842 PurpleChat
*chat
= (PurpleChat
*)node
;
843 name
= purple_chat_get_name(chat
);
845 strncpy(status
, "~", sizeof(status
) - 1);
847 else if (PURPLE_IS_GROUP(node
))
848 return purple_group_get_name((PurpleGroup
*)node
);
850 g_snprintf(text
, sizeof(text
) - 1, "%s %s", status
, name
);
856 add_chat(PurpleChat
*chat
, FinchBuddyList
*ggblist
)
859 PurpleBlistNode
*node
= (PurpleBlistNode
*)chat
;
860 if (purple_blist_node_get_ui_data(node
))
862 if (!purple_account_is_connected(purple_chat_get_account(chat
)))
865 parent
= ggblist
->manager
->find_parent((PurpleBlistNode
*)chat
);
867 create_finch_blist_node(node
, gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), chat
,
868 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), get_display_name(node
)),
873 add_contact(PurpleContact
*contact
, FinchBuddyList
*ggblist
)
876 PurpleBlistNode
*node
= (PurpleBlistNode
*)contact
;
879 if (purple_blist_node_get_ui_data(node
))
882 name
= get_display_name(node
);
886 parent
= ggblist
->manager
->find_parent((PurpleBlistNode
*)contact
);
888 create_finch_blist_node(node
, gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), contact
,
889 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), name
),
892 gnt_tree_set_expanded(GNT_TREE(ggblist
->tree
), contact
, FALSE
);
896 add_buddy(PurpleBuddy
*buddy
, FinchBuddyList
*ggblist
)
899 PurpleBlistNode
*node
= (PurpleBlistNode
*)buddy
;
900 PurpleContact
*contact
;
902 if (purple_blist_node_get_ui_data(node
))
905 contact
= purple_buddy_get_contact(buddy
);
906 parent
= ggblist
->manager
->find_parent((PurpleBlistNode
*)buddy
);
908 create_finch_blist_node(node
, gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), buddy
,
909 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), get_display_name(node
)),
912 blist_update_row_flags(ggblist
, (PurpleBlistNode
*)buddy
);
913 if (buddy
== purple_contact_get_priority_buddy(contact
)) {
914 blist_update_row_flags(ggblist
, (PurpleBlistNode
*)contact
);
919 selection_activate(GntWidget
*widget
, FinchBuddyList
*ggblist
)
921 GntTree
*tree
= GNT_TREE(ggblist
->tree
);
922 PurpleBlistNode
*node
= gnt_tree_get_selection_data(tree
);
927 if (PURPLE_IS_CONTACT(node
))
928 node
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
)));
930 if (PURPLE_IS_BUDDY(node
))
932 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
933 PurpleIMConversation
*im
;
934 im
= purple_conversations_find_im_with_account(purple_buddy_get_name(buddy
),
935 purple_buddy_get_account(buddy
));
937 im
= purple_im_conversation_new(purple_buddy_get_account(buddy
),
938 purple_buddy_get_name(buddy
));
940 FinchConv
*ggconv
= FINCH_CONV(PURPLE_CONVERSATION(im
));
941 gnt_window_present(ggconv
->window
);
943 finch_conversation_set_active(PURPLE_CONVERSATION(im
));
945 else if (PURPLE_IS_CHAT(node
))
947 join_chat((PurpleChat
*)node
);
952 append_proto_menu(GntMenu
*menu
, PurpleConnection
*gc
, PurpleBlistNode
*node
)
955 PurpleProtocol
*protocol
= purple_connection_get_protocol(gc
);
957 if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol
, CLIENT
, blist_node_menu
)) {
961 for(list
= purple_protocol_client_iface_blist_node_menu(protocol
, node
); list
;
962 list
= g_list_delete_link(list
, list
))
964 PurpleActionMenu
*act
= (PurpleActionMenu
*) list
->data
;
967 purple_action_menu_set_data(act
, node
);
968 finch_append_menu_action(menu
, act
, node
);
973 add_custom_action(GntMenu
*menu
, const char *label
, PurpleCallback callback
,
976 PurpleActionMenu
*action
= purple_action_menu_new(label
, callback
, data
, NULL
);
977 finch_append_menu_action(menu
, action
, NULL
);
981 chat_components_edit_ok(PurpleChat
*chat
, PurpleRequestFields
*allfields
)
983 GList
*groups
, *fields
;
985 for (groups
= purple_request_fields_get_groups(allfields
); groups
; groups
= groups
->next
) {
986 fields
= purple_request_field_group_get_fields(groups
->data
);
987 for (; fields
; fields
= fields
->next
) {
988 PurpleRequestField
*field
= fields
->data
;
992 id
= purple_request_field_get_id(field
);
993 if (purple_request_field_get_field_type(field
) == PURPLE_REQUEST_FIELD_INTEGER
)
994 val
= g_strdup_printf("%d", purple_request_field_int_get_value(field
));
996 val
= g_strdup(purple_request_field_string_get_value(field
));
999 g_hash_table_remove(purple_chat_get_components(chat
), id
);
1001 g_hash_table_replace(purple_chat_get_components(chat
), g_strdup(id
), val
); /* val should not be free'd */
1008 chat_components_edit(PurpleBlistNode
*selected
, PurpleChat
*chat
)
1010 PurpleRequestFields
*fields
= purple_request_fields_new();
1011 PurpleRequestFieldGroup
*group
= purple_request_field_group_new(NULL
);
1012 PurpleRequestField
*field
;
1013 GList
*parts
, *iter
;
1014 PurpleProtocolChatEntry
*pce
;
1015 PurpleConnection
*gc
;
1017 purple_request_fields_add_group(fields
, group
);
1019 gc
= purple_account_get_connection(purple_chat_get_account(chat
));
1020 parts
= purple_protocol_chat_iface_info(purple_connection_get_protocol(gc
), gc
);
1022 for (iter
= parts
; iter
; iter
= iter
->next
) {
1026 const char *str
= g_hash_table_lookup(purple_chat_get_components(chat
), pce
->identifier
);
1027 if (!str
|| sscanf(str
, "%d", &val
) != 1)
1029 field
= purple_request_field_int_new(pce
->identifier
, pce
->label
, val
, INT_MIN
, INT_MAX
);
1031 field
= purple_request_field_string_new(pce
->identifier
, pce
->label
,
1032 g_hash_table_lookup(purple_chat_get_components(chat
), pce
->identifier
), FALSE
);
1034 purple_request_field_string_set_masked(field
, TRUE
);
1038 purple_request_field_set_required(field
, TRUE
);
1040 purple_request_field_group_add_field(group
, field
);
1046 purple_request_fields(NULL
, _("Edit Chat"), NULL
, _("Please Update the necessary fields."),
1047 fields
, _("Edit"), G_CALLBACK(chat_components_edit_ok
), _("Cancel"), NULL
,
1052 autojoin_toggled(GntMenuItem
*item
, gpointer data
)
1054 PurpleActionMenu
*action
= data
;
1055 purple_blist_node_set_bool(purple_action_menu_get_data(action
), "gnt-autojoin",
1056 gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item
)));
1060 create_chat_menu(GntMenu
*menu
, PurpleChat
*chat
)
1062 PurpleActionMenu
*action
= purple_action_menu_new(_("Auto-join"), NULL
, chat
, NULL
);
1063 GntMenuItem
*check
= gnt_menuitem_check_new(
1064 purple_action_menu_get_label(action
));
1065 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(check
),
1066 purple_blist_node_get_bool((PurpleBlistNode
*)chat
, "gnt-autojoin"));
1067 gnt_menu_add_item(menu
, check
);
1068 gnt_menuitem_set_callback(check
, autojoin_toggled
, action
);
1069 g_signal_connect_swapped(G_OBJECT(menu
), "destroy",
1070 G_CALLBACK(purple_action_menu_free
), action
);
1072 /* Protocol actions */
1073 append_proto_menu(menu
,
1074 purple_account_get_connection(purple_chat_get_account(chat
)),
1075 (PurpleBlistNode
*)chat
);
1077 add_custom_action(menu
, _("Edit Settings"), (PurpleCallback
)chat_components_edit
, chat
);
1081 finch_add_buddy(PurpleBlistNode
*selected
, PurpleGroup
*grp
)
1083 purple_blist_request_add_buddy(NULL
, NULL
, grp
? purple_group_get_name(grp
) : NULL
, NULL
);
1087 finch_add_group(PurpleBlistNode
*selected
, PurpleGroup
*grp
)
1089 purple_blist_request_add_group();
1093 finch_add_chat(PurpleBlistNode
*selected
, PurpleGroup
*grp
)
1095 purple_blist_request_add_chat(NULL
, grp
, NULL
, NULL
);
1099 create_group_menu(GntMenu
*menu
, PurpleGroup
*group
)
1101 add_custom_action(menu
, _("Add Buddy"),
1102 PURPLE_CALLBACK(finch_add_buddy
), group
);
1103 add_custom_action(menu
, _("Add Chat"),
1104 PURPLE_CALLBACK(finch_add_chat
), group
);
1105 add_custom_action(menu
, _("Add Group"),
1106 PURPLE_CALLBACK(finch_add_group
), group
);
1109 gpointer
finch_retrieve_user_info(PurpleConnection
*conn
, const char *name
)
1111 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
1113 purple_notify_user_info_add_pair_plaintext(info
, _("Information"), _("Retrieving..."));
1114 uihandle
= purple_notify_userinfo(conn
, name
, info
, NULL
, NULL
);
1115 purple_notify_user_info_destroy(info
);
1117 purple_serv_get_info(conn
, name
);
1122 finch_blist_get_buddy_info_cb(PurpleBlistNode
*selected
, PurpleBuddy
*buddy
)
1124 finch_retrieve_user_info(purple_account_get_connection(purple_buddy_get_account(buddy
)), purple_buddy_get_name(buddy
));
1128 finch_blist_menu_send_file_cb(PurpleBlistNode
*selected
, PurpleBuddy
*buddy
)
1130 purple_serv_send_file(purple_account_get_connection(purple_buddy_get_account(buddy
)), purple_buddy_get_name(buddy
), NULL
);
1134 finch_blist_pounce_node_cb(PurpleBlistNode
*selected
, PurpleBlistNode
*node
)
1137 if (PURPLE_IS_CONTACT(node
))
1138 b
= purple_contact_get_priority_buddy((PurpleContact
*)node
);
1140 b
= (PurpleBuddy
*)node
;
1141 finch_pounce_editor_show(purple_buddy_get_account(b
), purple_buddy_get_name(b
), NULL
);
1145 toggle_block_buddy(GntMenuItem
*item
, gpointer buddy
)
1147 gboolean block
= gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item
));
1148 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1149 const char *name
= purple_buddy_get_name(buddy
);
1151 block
? purple_account_privacy_deny(account
, name
) :
1152 purple_account_privacy_allow(account
, name
);
1156 toggle_show_offline(GntMenuItem
*item
, gpointer buddy
)
1158 purple_blist_node_set_bool(buddy
, "show_offline",
1159 !purple_blist_node_get_bool(buddy
, "show_offline"));
1160 if (!ggblist
->manager
->can_add_node(buddy
))
1161 node_remove(purple_blist_get_default(), buddy
);
1163 node_update(purple_blist_get_default(), buddy
);
1167 create_buddy_menu(GntMenu
*menu
, PurpleBuddy
*buddy
)
1169 PurpleAccount
*account
;
1172 PurpleProtocol
*protocol
;
1173 PurpleConnection
*gc
= purple_account_get_connection(purple_buddy_get_account(buddy
));
1175 protocol
= purple_connection_get_protocol(gc
);
1176 if (protocol
&& PURPLE_PROTOCOL_IMPLEMENTS(protocol
, SERVER
, get_info
))
1178 add_custom_action(menu
, _("Get Info"),
1179 PURPLE_CALLBACK(finch_blist_get_buddy_info_cb
), buddy
);
1182 add_custom_action(menu
, _("Add Buddy Pounce"),
1183 PURPLE_CALLBACK(finch_blist_pounce_node_cb
), buddy
);
1185 if (PURPLE_IS_PROTOCOL_XFER(protocol
))
1187 if (purple_protocol_xfer_can_receive(
1188 PURPLE_PROTOCOL_XFER(protocol
),
1190 purple_buddy_get_name(buddy
))
1192 add_custom_action(menu
, _("Send File"),
1193 PURPLE_CALLBACK(finch_blist_menu_send_file_cb
), buddy
);
1197 account
= purple_buddy_get_account(buddy
);
1198 permitted
= purple_account_privacy_check(account
, purple_buddy_get_name(buddy
));
1200 item
= gnt_menuitem_check_new(_("Blocked"));
1201 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item
), !permitted
);
1202 gnt_menuitem_set_callback(item
, toggle_block_buddy
, buddy
);
1203 gnt_menu_add_item(menu
, item
);
1205 item
= gnt_menuitem_check_new(_("Show when offline"));
1206 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item
), purple_blist_node_get_bool((PurpleBlistNode
*)buddy
, "show_offline"));
1207 gnt_menuitem_set_callback(item
, toggle_show_offline
, buddy
);
1208 gnt_menu_add_item(menu
, item
);
1210 /* Protocol actions */
1211 append_proto_menu(menu
,
1212 purple_account_get_connection(purple_buddy_get_account(buddy
)),
1213 (PurpleBlistNode
*)buddy
);
1217 append_extended_menu(GntMenu
*menu
, PurpleBlistNode
*node
)
1221 for (iter
= purple_blist_node_get_extended_menu(node
);
1222 iter
; iter
= g_list_delete_link(iter
, iter
))
1224 finch_append_menu_action(menu
, iter
->data
, node
);
1228 /* Xerox'd from gtkdialogs.c:purple_gtkdialogs_remove_contact_cb */
1230 remove_contact(PurpleContact
*contact
)
1232 PurpleBlistNode
*bnode
, *cnode
;
1235 cnode
= (PurpleBlistNode
*)contact
;
1236 group
= (PurpleGroup
*)purple_blist_node_get_parent(cnode
);
1237 for (bnode
= purple_blist_node_get_first_child(cnode
); bnode
; bnode
= purple_blist_node_get_sibling_next(bnode
)) {
1238 PurpleBuddy
*buddy
= (PurpleBuddy
*)bnode
;
1239 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1240 if (purple_account_is_connected(account
))
1241 purple_account_remove_buddy(account
, buddy
, group
);
1243 purple_blist_remove_contact(contact
);
1247 rename_blist_node(PurpleBlistNode
*node
, const char *newname
)
1249 const char *name
= newname
;
1253 if (PURPLE_IS_CONTACT(node
)) {
1254 PurpleContact
*contact
= (PurpleContact
*)node
;
1255 PurpleBuddy
*buddy
= purple_contact_get_priority_buddy(contact
);
1256 purple_contact_set_alias(contact
, name
);
1257 purple_buddy_set_local_alias(buddy
, name
);
1258 purple_serv_alias_buddy(buddy
);
1259 } else if (PURPLE_IS_BUDDY(node
)) {
1260 purple_buddy_set_local_alias((PurpleBuddy
*)node
, name
);
1261 purple_serv_alias_buddy((PurpleBuddy
*)node
);
1262 } else if (PURPLE_IS_CHAT(node
))
1263 purple_chat_set_alias((PurpleChat
*)node
, name
);
1264 else if (PURPLE_IS_GROUP(node
) && (name
!= NULL
))
1265 purple_group_set_name((PurpleGroup
*)node
, name
);
1267 g_return_if_reached();
1271 finch_blist_rename_node_cb(PurpleBlistNode
*selected
, PurpleBlistNode
*node
)
1273 const char *name
= NULL
;
1277 if (PURPLE_IS_CONTACT(node
))
1278 name
= purple_contact_get_alias((PurpleContact
*)node
);
1279 else if (PURPLE_IS_BUDDY(node
))
1280 name
= purple_buddy_get_contact_alias((PurpleBuddy
*)node
);
1281 else if (PURPLE_IS_CHAT(node
))
1282 name
= purple_chat_get_name((PurpleChat
*)node
);
1283 else if (PURPLE_IS_GROUP(node
))
1284 name
= purple_group_get_name((PurpleGroup
*)node
);
1286 g_return_if_reached();
1288 prompt
= g_strdup_printf(_("Please enter the new name for %s"), name
);
1290 text
= PURPLE_IS_GROUP(node
) ? _("Rename") : _("Set Alias");
1291 purple_request_input(node
, text
, prompt
, _("Enter empty string to reset the name."),
1292 name
, FALSE
, FALSE
, NULL
, text
, G_CALLBACK(rename_blist_node
),
1300 static void showlog_cb(PurpleBlistNode
*sel
, PurpleBlistNode
*node
)
1303 PurpleAccount
*account
;
1306 if (PURPLE_IS_BUDDY(node
)) {
1307 PurpleBuddy
*b
= (PurpleBuddy
*) node
;
1308 type
= PURPLE_LOG_IM
;
1309 name
= g_strdup(purple_buddy_get_name(b
));
1310 account
= purple_buddy_get_account(b
);
1311 } else if (PURPLE_IS_CHAT(node
)) {
1312 PurpleChat
*c
= (PurpleChat
*) node
;
1313 PurpleProtocol
*protocol
= NULL
;
1314 type
= PURPLE_LOG_CHAT
;
1315 account
= purple_chat_get_account(c
);
1316 protocol
= purple_protocols_find(purple_account_get_protocol_id(account
));
1318 name
= purple_protocol_chat_iface_get_name(protocol
, purple_chat_get_components(c
));
1320 } else if (PURPLE_IS_CONTACT(node
)) {
1321 finch_log_show_contact((PurpleContact
*)node
);
1324 /* This callback should not have been registered for a node
1325 * that doesn't match the type of one of the blocks above. */
1326 g_return_if_reached();
1329 if (name
&& account
) {
1330 finch_log_show(type
, name
, account
);
1336 /* Xeroxed from gtkdialogs.c:purple_gtkdialogs_remove_group_cb*/
1338 remove_group(PurpleGroup
*group
)
1340 PurpleBlistNode
*cnode
, *bnode
;
1342 cnode
= purple_blist_node_get_first_child(((PurpleBlistNode
*)group
));
1345 if (PURPLE_IS_CONTACT(cnode
)) {
1346 bnode
= purple_blist_node_get_first_child(cnode
);
1347 cnode
= purple_blist_node_get_sibling_next(cnode
);
1350 if (PURPLE_IS_BUDDY(bnode
)) {
1351 PurpleAccount
*account
;
1352 buddy
= (PurpleBuddy
*)bnode
;
1353 bnode
= purple_blist_node_get_sibling_next(bnode
);
1354 account
= purple_buddy_get_account(buddy
);
1355 if (purple_account_is_connected(account
)) {
1356 purple_account_remove_buddy(account
, buddy
, group
);
1357 purple_blist_remove_buddy(buddy
);
1360 bnode
= purple_blist_node_get_sibling_next(bnode
);
1363 } else if (PURPLE_IS_CHAT(cnode
)) {
1364 PurpleChat
*chat
= (PurpleChat
*)cnode
;
1365 cnode
= purple_blist_node_get_sibling_next(cnode
);
1366 if (purple_account_is_connected(purple_chat_get_account(chat
)))
1367 purple_blist_remove_chat(chat
);
1369 cnode
= purple_blist_node_get_sibling_next(cnode
);
1373 purple_blist_remove_group(group
);
1377 finch_blist_remove_node(PurpleBlistNode
*node
)
1379 if (PURPLE_IS_CONTACT(node
)) {
1380 remove_contact((PurpleContact
*)node
);
1381 } else if (PURPLE_IS_BUDDY(node
)) {
1382 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
1383 PurpleGroup
*group
= purple_buddy_get_group(buddy
);
1384 purple_account_remove_buddy(purple_buddy_get_account(buddy
), buddy
, group
);
1385 purple_blist_remove_buddy(buddy
);
1386 } else if (PURPLE_IS_CHAT(node
)) {
1387 purple_blist_remove_chat((PurpleChat
*)node
);
1388 } else if (PURPLE_IS_GROUP(node
)) {
1389 remove_group((PurpleGroup
*)node
);
1394 finch_blist_remove_node_cb(PurpleBlistNode
*selected
, PurpleBlistNode
*node
)
1396 PurpleAccount
*account
= NULL
;
1398 const char *name
, *sec
= NULL
;
1400 if (PURPLE_IS_CONTACT(node
)) {
1401 PurpleContact
*c
= (PurpleContact
*)node
;
1402 name
= purple_contact_get_alias(c
);
1403 if (purple_counting_node_get_total_size(PURPLE_COUNTING_NODE(c
)) > 1)
1404 sec
= _("Removing this contact will also remove all the buddies in the contact");
1405 } else if (PURPLE_IS_BUDDY(node
)) {
1406 name
= purple_buddy_get_name((PurpleBuddy
*)node
);
1407 account
= purple_buddy_get_account((PurpleBuddy
*)node
);
1408 } else if (PURPLE_IS_CHAT(node
)) {
1409 name
= purple_chat_get_name((PurpleChat
*)node
);
1410 } else if (PURPLE_IS_GROUP(node
)) {
1411 name
= purple_group_get_name((PurpleGroup
*)node
);
1412 sec
= _("Removing this group will also remove all the buddies in the group");
1417 primary
= g_strdup_printf(_("Are you sure you want to remove %s?"), name
);
1419 /* XXX: anything to do with the returned ui-handle? */
1420 purple_request_action(node
, _("Confirm Remove"),
1423 purple_request_cpar_from_account(account
),
1425 _("Remove"), finch_blist_remove_node
,
1431 finch_blist_toggle_tag_buddy(PurpleBlistNode
*node
)
1436 if (ggblist
->tagged
&& (iter
= g_list_find(ggblist
->tagged
, node
)) != NULL
) {
1437 ggblist
->tagged
= g_list_delete_link(ggblist
->tagged
, iter
);
1439 ggblist
->tagged
= g_list_prepend(ggblist
->tagged
, node
);
1441 if (PURPLE_IS_CONTACT(node
))
1442 update_buddy_display(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
)), ggblist
);
1443 else if (PURPLE_IS_BUDDY(node
))
1444 update_buddy_display((PurpleBuddy
*)node
, ggblist
);
1446 update_node_display(node
, ggblist
);
1450 finch_blist_place_tagged(PurpleBlistNode
*target
)
1452 PurpleGroup
*tg
= NULL
;
1453 PurpleContact
*tc
= NULL
;
1455 if (PURPLE_IS_GROUP(target
))
1456 tg
= (PurpleGroup
*)target
;
1457 else if (PURPLE_IS_BUDDY(target
)) {
1458 tc
= (PurpleContact
*)purple_blist_node_get_parent(target
);
1459 tg
= (PurpleGroup
*)purple_blist_node_get_parent((PurpleBlistNode
*)tc
);
1460 } else if (PURPLE_IS_CONTACT(target
)) {
1461 tc
= (PurpleContact
*)target
;
1462 tg
= (PurpleGroup
*)purple_blist_node_get_parent(target
);
1463 } else if (PURPLE_IS_CHAT(target
)) {
1464 tg
= (PurpleGroup
*)purple_blist_node_get_parent(target
);
1469 if (ggblist
->tagged
) {
1470 GList
*list
= ggblist
->tagged
;
1471 ggblist
->tagged
= NULL
;
1473 PurpleBlistNode
*node
= list
->data
;
1474 list
= g_list_delete_link(list
, list
);
1476 if (PURPLE_IS_GROUP(node
)) {
1477 update_node_display(node
, ggblist
);
1478 /* Add the group after the current group */
1479 purple_blist_add_group((PurpleGroup
*)node
, (PurpleBlistNode
*)tg
);
1480 } else if (PURPLE_IS_CONTACT(node
)) {
1481 update_buddy_display(purple_contact_get_priority_buddy((PurpleContact
*)node
), ggblist
);
1482 if (PURPLE_BLIST_NODE(tg
) == target
) {
1483 /* The target is a group, just add the contact to the group. */
1484 purple_blist_add_contact((PurpleContact
*)node
, tg
, NULL
);
1486 /* The target is either a buddy, or a contact. Merge with that contact. */
1487 purple_contact_merge((PurpleContact
*)node
, (PurpleBlistNode
*)tc
);
1489 /* The target is a chat. Add the contact to the group after this chat. */
1490 purple_blist_add_contact((PurpleContact
*)node
, NULL
, target
);
1492 } else if (PURPLE_IS_BUDDY(node
)) {
1493 update_buddy_display((PurpleBuddy
*)node
, ggblist
);
1494 if (PURPLE_BLIST_NODE(tg
) == target
) {
1495 /* The target is a group. Add this buddy in a new contact under this group. */
1496 purple_blist_add_buddy((PurpleBuddy
*)node
, NULL
, tg
, NULL
);
1497 } else if (PURPLE_IS_CONTACT(target
)) {
1498 /* Add to the contact. */
1499 purple_blist_add_buddy((PurpleBuddy
*)node
, tc
, NULL
, NULL
);
1500 } else if (PURPLE_IS_BUDDY(target
)) {
1501 /* Add to the contact after the selected buddy. */
1502 purple_blist_add_buddy((PurpleBuddy
*)node
, NULL
, NULL
, target
);
1503 } else if (PURPLE_IS_CHAT(target
)) {
1504 /* Add to the selected chat's group. */
1505 purple_blist_add_buddy((PurpleBuddy
*)node
, NULL
, tg
, NULL
);
1507 } else if (PURPLE_IS_CHAT(node
)) {
1508 update_node_display(node
, ggblist
);
1509 if (PURPLE_BLIST_NODE(tg
) == target
)
1510 purple_blist_add_chat((PurpleChat
*)node
, tg
, NULL
);
1512 purple_blist_add_chat((PurpleChat
*)node
, NULL
, target
);
1519 context_menu_destroyed(GntWidget
*widget
, FinchBuddyList
*ggblist
)
1521 ggblist
->context
= NULL
;
1525 draw_context_menu(FinchBuddyList
*ggblist
)
1527 PurpleBlistNode
*node
= NULL
;
1528 GntWidget
*context
= NULL
;
1529 GntTree
*tree
= NULL
;
1530 int x
, y
, top
, width
;
1533 if (ggblist
->context
)
1536 tree
= GNT_TREE(ggblist
->tree
);
1538 node
= gnt_tree_get_selection_data(tree
);
1539 if (node
&& !(PURPLE_IS_BUDDY(node
) || PURPLE_IS_CONTACT(node
) ||
1540 PURPLE_IS_GROUP(node
) || PURPLE_IS_CHAT(node
)))
1543 if (ggblist
->tooltip
)
1544 remove_tooltip(ggblist
);
1546 ggblist
->cnode
= node
;
1548 ggblist
->context
= context
= gnt_menu_new(GNT_MENU_POPUP
);
1549 g_signal_connect(G_OBJECT(context
), "destroy", G_CALLBACK(context_menu_destroyed
), ggblist
);
1550 g_signal_connect(G_OBJECT(context
), "hide", G_CALLBACK(gnt_widget_destroy
), NULL
);
1553 create_group_menu(GNT_MENU(context
), NULL
);
1554 title
= g_strdup(_("Buddy List"));
1555 } else if (PURPLE_IS_CONTACT(node
)) {
1556 ggblist
->cnode
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
)));
1557 create_buddy_menu(GNT_MENU(context
), (PurpleBuddy
*)ggblist
->cnode
);
1558 title
= g_strdup(purple_contact_get_alias((PurpleContact
*)node
));
1559 } else if (PURPLE_IS_BUDDY(node
)) {
1560 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
1561 create_buddy_menu(GNT_MENU(context
), buddy
);
1562 title
= g_strdup(purple_buddy_get_name(buddy
));
1563 } else if (PURPLE_IS_CHAT(node
)) {
1564 PurpleChat
*chat
= (PurpleChat
*)node
;
1565 create_chat_menu(GNT_MENU(context
), chat
);
1566 title
= g_strdup(purple_chat_get_name(chat
));
1567 } else if (PURPLE_IS_GROUP(node
)) {
1568 PurpleGroup
*group
= (PurpleGroup
*)node
;
1569 create_group_menu(GNT_MENU(context
), group
);
1570 title
= g_strdup(purple_group_get_name(group
));
1573 append_extended_menu(GNT_MENU(context
), node
);
1575 /* These are common for everything */
1577 add_custom_action(GNT_MENU(context
),
1578 PURPLE_IS_GROUP(node
) ? _("Rename") : _("Alias"),
1579 PURPLE_CALLBACK(finch_blist_rename_node_cb
), node
);
1580 add_custom_action(GNT_MENU(context
), _("Remove"),
1581 PURPLE_CALLBACK(finch_blist_remove_node_cb
), node
);
1583 if (ggblist
->tagged
&& (PURPLE_IS_CONTACT(node
)
1584 || PURPLE_IS_GROUP(node
))) {
1585 add_custom_action(GNT_MENU(context
), _("Place tagged"),
1586 PURPLE_CALLBACK(finch_blist_place_tagged
), node
);
1589 if (PURPLE_IS_BUDDY(node
) || PURPLE_IS_CONTACT(node
)) {
1590 add_custom_action(GNT_MENU(context
), _("Toggle Tag"),
1591 PURPLE_CALLBACK(finch_blist_toggle_tag_buddy
), node
);
1593 if (!PURPLE_IS_GROUP(node
)) {
1594 add_custom_action(GNT_MENU(context
), _("View Log"),
1595 PURPLE_CALLBACK(showlog_cb
), node
);
1599 /* Set the position for the popup */
1600 gnt_widget_get_position(GNT_WIDGET(tree
), &x
, &y
);
1601 gnt_widget_get_size(GNT_WIDGET(tree
), &width
, NULL
);
1602 top
= gnt_tree_get_selection_visible_line(tree
);
1607 gnt_widget_set_position(context
, x
, y
);
1608 gnt_screen_menu_show(GNT_MENU(context
));
1613 tooltip_for_buddy(PurpleBuddy
*buddy
, GString
*str
, gboolean full
)
1615 PurpleProtocol
*protocol
;
1616 PurpleAccount
*account
;
1617 PurpleNotifyUserInfo
*user_info
;
1618 PurplePresence
*presence
;
1619 const char *alias
= purple_buddy_get_alias(buddy
);
1622 user_info
= purple_notify_user_info_new();
1624 account
= purple_buddy_get_account(buddy
);
1625 presence
= purple_buddy_get_presence(buddy
);
1627 if (!full
|| g_utf8_collate(purple_buddy_get_name(buddy
), alias
)) {
1628 purple_notify_user_info_add_pair_plaintext(user_info
, _("Nickname"), alias
);
1631 tmp
= g_strdup_printf("%s (%s)",
1632 purple_account_get_username(account
),
1633 purple_account_get_protocol_name(account
));
1634 purple_notify_user_info_add_pair_plaintext(user_info
, _("Account"), tmp
);
1637 protocol
= purple_protocols_find(purple_account_get_protocol_id(account
));
1639 purple_protocol_client_iface_tooltip_text(protocol
, buddy
, user_info
, full
);
1642 if (purple_prefs_get_bool("/finch/blist/idletime")) {
1643 PurplePresence
*pre
= purple_buddy_get_presence(buddy
);
1644 if (purple_presence_is_idle(pre
)) {
1645 time_t idle
= purple_presence_get_idle_time(pre
);
1647 char *st
= purple_str_seconds_to_string(time(NULL
) - idle
);
1648 purple_notify_user_info_add_pair_plaintext(user_info
, _("Idle"), st
);
1654 tmp
= purple_notify_user_info_get_text_with_newline(user_info
, "<BR>");
1655 purple_notify_user_info_destroy(user_info
);
1657 strip
= purple_markup_strip_html(tmp
);
1658 g_string_append(str
, strip
);
1660 if (purple_presence_is_status_primitive_active(presence
, PURPLE_STATUS_MOBILE
)) {
1661 g_string_append(str
, "\n");
1662 g_string_append(str
, _("On Mobile"));
1670 make_sure_text_fits(GString
*string
)
1672 int maxw
= getmaxx(stdscr
) - 3;
1673 char *str
= gnt_util_onscreen_fit_string(string
->str
, maxw
);
1674 string
= g_string_assign(string
, str
);
1680 draw_tooltip_real(FinchBuddyList
*ggblist
)
1682 PurpleBlistNode
*node
;
1683 int x
, y
, top
, width
, w
, h
;
1684 GString
*str
= NULL
;
1686 GntWidget
*widget
, *box
, *tv
;
1689 widget
= ggblist
->tree
;
1690 tree
= GNT_TREE(widget
);
1692 if (!gnt_widget_has_focus(ggblist
->tree
) ||
1693 (ggblist
->context
&& gnt_widget_get_visible(ggblist
->context
)))
1696 if (ggblist
->tooltip
)
1698 /* XXX: Once we can properly redraw on expose events, this can be removed at the end
1699 * to avoid the blinking*/
1700 remove_tooltip(ggblist
);
1703 node
= gnt_tree_get_selection_data(tree
);
1707 if (!ggblist
->manager
->create_tooltip(node
, &str
, &title
))
1710 gnt_widget_get_position(widget
, &x
, &y
);
1711 gnt_widget_get_size(widget
, &width
, NULL
);
1712 top
= gnt_tree_get_selection_visible_line(tree
);
1717 box
= gnt_box_new(FALSE
, FALSE
);
1718 gnt_box_set_toplevel(GNT_BOX(box
), TRUE
);
1719 gnt_widget_set_has_shadow(box
, FALSE
);
1720 gnt_box_set_title(GNT_BOX(box
), title
);
1722 str
= make_sure_text_fits(str
);
1723 gnt_util_get_text_bound(str
->str
, &w
, &h
);
1725 tv
= gnt_text_view_new();
1726 gnt_widget_set_size(tv
, w
+ 1, h
);
1727 gnt_text_view_set_flag(GNT_TEXT_VIEW(tv
), GNT_TEXT_VIEW_NO_SCROLL
);
1728 gnt_box_add_widget(GNT_BOX(box
), tv
);
1730 if (x
+ w
>= getmaxx(stdscr
))
1732 gnt_widget_set_position(box
, x
, y
);
1733 gnt_widget_set_take_focus(box
, FALSE
);
1734 gnt_widget_set_transient(box
, TRUE
);
1735 gnt_widget_draw(box
);
1737 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(tv
), str
->str
, GNT_TEXT_FLAG_NORMAL
);
1738 gnt_text_view_scroll(GNT_TEXT_VIEW(tv
), 0);
1741 g_string_free(str
, TRUE
);
1742 ggblist
->tooltip
= box
;
1743 ggblist
->tnode
= node
;
1745 gnt_widget_set_name(ggblist
->tooltip
, "tooltip");
1750 draw_tooltip(FinchBuddyList
*ggblist
)
1752 /* When an account has signed off, it removes one buddy at a time.
1753 * Drawing the tooltip after removing each buddy is expensive. On
1754 * top of that, if the selected buddy belongs to the disconnected
1755 * account, then retreiving the tooltip for that causes crash. So
1756 * let's make sure we wait for all the buddies to be removed first.*/
1757 int id
= g_timeout_add(0, (GSourceFunc
)draw_tooltip_real
, ggblist
);
1758 g_object_set_data_full(G_OBJECT(ggblist
->window
), "draw_tooltip_calback",
1759 GINT_TO_POINTER(id
), (GDestroyNotify
)g_source_remove
);
1763 selection_changed(GntWidget
*widget
, gpointer old
, gpointer current
,
1764 FinchBuddyList
*ggblist
)
1766 remove_peripherals(ggblist
);
1767 draw_tooltip(ggblist
);
1771 context_menu(GntWidget
*widget
, FinchBuddyList
*ggblist
)
1773 draw_context_menu(ggblist
);
1778 key_pressed(GntWidget
*widget
, const char *text
, FinchBuddyList
*ggblist
)
1780 if (text
[0] == 27 && text
[1] == 0) {
1781 /* Escape was pressed */
1782 if (gnt_tree_is_searching(GNT_TREE(ggblist
->tree
)))
1783 gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist
->tree
), "end-search", NULL
);
1784 remove_peripherals(ggblist
);
1785 } else if (purple_strequal(text
, GNT_KEY_INS
)) {
1786 PurpleBlistNode
*node
= gnt_tree_get_selection_data(GNT_TREE(ggblist
->tree
));
1787 purple_blist_request_add_buddy(NULL
, NULL
,
1788 node
&& PURPLE_IS_GROUP(node
) ? purple_group_get_name(PURPLE_GROUP(node
)) : NULL
,
1790 } else if (!gnt_tree_is_searching(GNT_TREE(ggblist
->tree
))) {
1791 if (purple_strequal(text
, "t")) {
1792 finch_blist_toggle_tag_buddy(gnt_tree_get_selection_data(GNT_TREE(ggblist
->tree
)));
1793 gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist
->tree
), "move-down", NULL
);
1794 } else if (purple_strequal(text
, "a")) {
1795 finch_blist_place_tagged(gnt_tree_get_selection_data(GNT_TREE(ggblist
->tree
)));
1805 update_node_display(PurpleBlistNode
*node
, FinchBuddyList
*ggblist
)
1807 GntTextFormatFlags flag
= get_blist_node_flag(ggblist
, node
);
1808 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), node
, flag
);
1812 update_buddy_display(PurpleBuddy
*buddy
, FinchBuddyList
*ggblist
)
1814 PurpleContact
*contact
;
1816 contact
= purple_buddy_get_contact(buddy
);
1818 gnt_tree_change_text(GNT_TREE(ggblist
->tree
), buddy
, 0, get_display_name((PurpleBlistNode
*)buddy
));
1819 gnt_tree_change_text(GNT_TREE(ggblist
->tree
), contact
, 0, get_display_name((PurpleBlistNode
*)contact
));
1821 blist_update_row_flags(ggblist
, (PurpleBlistNode
*)buddy
);
1822 if (buddy
== purple_contact_get_priority_buddy(contact
))
1823 blist_update_row_flags(ggblist
, (PurpleBlistNode
*)contact
);
1825 if (ggblist
->tnode
== (PurpleBlistNode
*)buddy
) {
1826 draw_tooltip(ggblist
);
1831 buddy_status_changed(PurpleBuddy
*buddy
, PurpleStatus
*old
, PurpleStatus
*now
,
1832 FinchBuddyList
*ggblist
)
1834 update_buddy_display(buddy
, ggblist
);
1838 buddy_idle_changed(PurpleBuddy
*buddy
, int old
, int new,
1839 FinchBuddyList
*ggblist
)
1841 update_buddy_display(buddy
, ggblist
);
1845 remove_peripherals(FinchBuddyList
*ggblist
)
1847 if (ggblist
->tooltip
)
1848 remove_tooltip(ggblist
);
1849 else if (ggblist
->context
)
1850 gnt_widget_destroy(ggblist
->context
);
1854 size_changed_cb(GntWidget
*w
, int wi
, int h
)
1857 gnt_widget_get_size(w
, &width
, &height
);
1858 purple_prefs_set_int(PREF_ROOT
"/size/width", width
);
1859 purple_prefs_set_int(PREF_ROOT
"/size/height", height
);
1863 save_position_cb(GntWidget
*w
, int x
, int y
)
1865 purple_prefs_set_int(PREF_ROOT
"/position/x", x
);
1866 purple_prefs_set_int(PREF_ROOT
"/position/y", y
);
1870 reset_blist_window(GntWidget
*window
, gpointer null
)
1872 PurpleBlistNode
*node
;
1873 purple_signals_disconnect_by_handle(finch_blist_get_handle());
1875 node
= purple_blist_get_default_root();
1877 reset_blist_node_ui_data(node
);
1878 node
= purple_blist_node_next(node
, TRUE
);
1881 if (ggblist
->typing
)
1882 g_source_remove(ggblist
->typing
);
1883 remove_peripherals(ggblist
);
1884 if (ggblist
->tagged
)
1885 g_list_free(ggblist
->tagged
);
1887 if (ggblist
->new_group_timeout
)
1888 g_source_remove(ggblist
->new_group_timeout
);
1889 if (ggblist
->new_group
)
1890 g_list_free(ggblist
->new_group
);
1896 populate_buddylist(void)
1898 PurpleBlistNode
*node
;
1899 PurpleBuddyList
*list
;
1901 if (ggblist
->manager
->init
)
1902 ggblist
->manager
->init();
1904 if (purple_strequal(purple_prefs_get_string(PREF_ROOT
"/sort_type"), "text")) {
1905 gnt_tree_set_compare_func(GNT_TREE(ggblist
->tree
),
1906 (GCompareFunc
)blist_node_compare_text
);
1907 } else if (purple_strequal(purple_prefs_get_string(PREF_ROOT
"/sort_type"), "status")) {
1908 gnt_tree_set_compare_func(GNT_TREE(ggblist
->tree
),
1909 (GCompareFunc
)blist_node_compare_status
);
1910 } else if (purple_strequal(purple_prefs_get_string(PREF_ROOT
"/sort_type"), "log")) {
1911 gnt_tree_set_compare_func(GNT_TREE(ggblist
->tree
),
1912 (GCompareFunc
)blist_node_compare_log
);
1915 list
= purple_blist_get_default();
1916 node
= purple_blist_get_root(list
);
1919 node_update(list
, node
);
1920 node
= purple_blist_node_next(node
, FALSE
);
1925 destroy_status_list(GList
*list
)
1927 g_list_free_full(list
, g_free
);
1931 populate_status_dropdown(void)
1935 GList
*items
= NULL
;
1936 StatusBoxItem
*item
= NULL
;
1938 /* First the primitives */
1939 PurpleStatusPrimitive prims
[] = {PURPLE_STATUS_AVAILABLE
, PURPLE_STATUS_AWAY
,
1940 PURPLE_STATUS_INVISIBLE
, PURPLE_STATUS_OFFLINE
, PURPLE_STATUS_UNSET
};
1942 gnt_combo_box_remove_all(GNT_COMBO_BOX(ggblist
->status
));
1944 for (i
= 0; prims
[i
] != PURPLE_STATUS_UNSET
; i
++)
1946 item
= g_new0(StatusBoxItem
, 1);
1947 item
->type
= STATUS_PRIMITIVE
;
1948 item
->u
.prim
= prims
[i
];
1949 items
= g_list_prepend(items
, item
);
1950 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
1951 purple_primitive_get_name_from_type(prims
[i
]));
1954 /* Now the popular statuses */
1955 for (iter
= purple_savedstatuses_get_popular(6); iter
; iter
= g_list_delete_link(iter
, iter
))
1957 item
= g_new0(StatusBoxItem
, 1);
1958 item
->type
= STATUS_SAVED_POPULAR
;
1959 item
->u
.saved
= iter
->data
;
1960 items
= g_list_prepend(items
, item
);
1961 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
1962 purple_savedstatus_get_title(iter
->data
));
1965 /* New savedstatus */
1966 item
= g_new0(StatusBoxItem
, 1);
1967 item
->type
= STATUS_SAVED_NEW
;
1968 items
= g_list_prepend(items
, item
);
1969 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
1972 /* More savedstatuses */
1973 item
= g_new0(StatusBoxItem
, 1);
1974 item
->type
= STATUS_SAVED_ALL
;
1975 items
= g_list_prepend(items
, item
);
1976 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
1979 /* The keys for the combobox are created here, and never used
1980 * anywhere else. So make sure the keys are freed when the widget
1982 g_object_set_data_full(G_OBJECT(ggblist
->status
), "list of statuses",
1983 items
, (GDestroyNotify
)destroy_status_list
);
1987 redraw_blist(const char *name
, PurplePrefType type
, gconstpointer val
, gpointer data
)
1989 PurpleBlistNode
*node
, *sel
;
1990 FinchBlistManager
*manager
;
1992 if (ggblist
== NULL
)
1995 manager
= finch_blist_manager_find(purple_prefs_get_string(PREF_ROOT
"/grouping"));
1996 if (manager
== NULL
)
1997 manager
= &default_manager
;
1998 if (ggblist
->manager
!= manager
) {
1999 if (ggblist
->manager
->uninit
)
2000 ggblist
->manager
->uninit();
2002 ggblist
->manager
= manager
;
2003 if (manager
->can_add_node
== NULL
)
2004 manager
->can_add_node
= default_can_add_node
;
2005 if (manager
->find_parent
== NULL
)
2006 manager
->find_parent
= default_find_parent
;
2007 if (manager
->create_tooltip
== NULL
)
2008 manager
->create_tooltip
= default_create_tooltip
;
2011 if (ggblist
->window
== NULL
)
2014 sel
= gnt_tree_get_selection_data(GNT_TREE(ggblist
->tree
));
2015 gnt_tree_remove_all(GNT_TREE(ggblist
->tree
));
2017 for (node
= purple_blist_get_default_root(); node
;
2018 node
= purple_blist_node_next(node
, TRUE
)) {
2019 reset_blist_node_ui_data(node
);
2021 populate_buddylist();
2022 gnt_tree_set_selected(GNT_TREE(ggblist
->tree
), sel
);
2023 draw_tooltip(ggblist
);
2026 void finch_blist_init()
2028 color_available
= gnt_style_get_color(NULL
, "color-available");
2029 if (!color_available
)
2030 color_available
= gnt_color_add_pair(COLOR_GREEN
, -1);
2031 color_away
= gnt_style_get_color(NULL
, "color-away");
2033 color_away
= gnt_color_add_pair(COLOR_BLUE
, -1);
2034 color_idle
= gnt_style_get_color(NULL
, "color-idle");
2036 color_idle
= gnt_color_add_pair(COLOR_CYAN
, -1);
2037 color_offline
= gnt_style_get_color(NULL
, "color-offline");
2039 color_offline
= gnt_color_add_pair(COLOR_RED
, -1);
2041 purple_prefs_add_none(PREF_ROOT
);
2042 purple_prefs_add_none(PREF_ROOT
"/size");
2043 purple_prefs_add_int(PREF_ROOT
"/size/width", 20);
2044 purple_prefs_add_int(PREF_ROOT
"/size/height", 17);
2045 purple_prefs_add_none(PREF_ROOT
"/position");
2046 purple_prefs_add_int(PREF_ROOT
"/position/x", 0);
2047 purple_prefs_add_int(PREF_ROOT
"/position/y", 0);
2048 purple_prefs_add_bool(PREF_ROOT
"/idletime", TRUE
);
2049 purple_prefs_add_bool(PREF_ROOT
"/showoffline", FALSE
);
2050 purple_prefs_add_bool(PREF_ROOT
"/emptygroups", FALSE
);
2051 purple_prefs_add_string(PREF_ROOT
"/sort_type", "text");
2052 purple_prefs_add_string(PREF_ROOT
"/grouping", "default");
2054 purple_prefs_connect_callback(finch_blist_get_handle(),
2055 PREF_ROOT
"/emptygroups", redraw_blist
, NULL
);
2056 purple_prefs_connect_callback(finch_blist_get_handle(),
2057 PREF_ROOT
"/showoffline", redraw_blist
, NULL
);
2058 purple_prefs_connect_callback(finch_blist_get_handle(),
2059 PREF_ROOT
"/sort_type", redraw_blist
, NULL
);
2060 purple_prefs_connect_callback(finch_blist_get_handle(),
2061 PREF_ROOT
"/grouping", redraw_blist
, NULL
);
2063 purple_signal_connect_priority(purple_connections_get_handle(),
2064 "autojoin", purple_blist_get_handle(),
2065 G_CALLBACK(account_autojoin_cb
), NULL
,
2066 PURPLE_SIGNAL_PRIORITY_HIGHEST
);
2068 finch_blist_install_manager(&default_manager
);
2074 remove_typing_cb(gpointer null
)
2076 PurpleSavedStatus
*current
;
2077 const char *message
, *newmessage
;
2078 char *escnewmessage
;
2079 PurpleStatusPrimitive prim
, newprim
;
2080 StatusBoxItem
*item
;
2082 current
= purple_savedstatus_get_current();
2083 message
= purple_savedstatus_get_message(current
);
2084 prim
= purple_savedstatus_get_primitive_type(current
);
2086 newmessage
= gnt_entry_get_text(GNT_ENTRY(ggblist
->statustext
));
2087 item
= gnt_combo_box_get_selected_data(GNT_COMBO_BOX(ggblist
->status
));
2088 escnewmessage
= newmessage
? g_markup_escape_text(newmessage
, -1) : NULL
;
2090 switch (item
->type
) {
2091 case STATUS_PRIMITIVE
:
2092 newprim
= item
->u
.prim
;
2094 case STATUS_SAVED_POPULAR
:
2095 newprim
= purple_savedstatus_get_primitive_type(item
->u
.saved
);
2098 goto end
; /* 'New' or 'Saved' is selected, but this should never happen. */
2101 if (newprim
!= prim
|| ((message
&& !escnewmessage
) ||
2102 (!message
&& escnewmessage
) ||
2103 (message
&& escnewmessage
&& g_utf8_collate(message
, escnewmessage
) != 0)))
2105 PurpleSavedStatus
*status
= purple_savedstatus_find_transient_by_type_and_message(newprim
, escnewmessage
);
2106 /* Holy Crap! That's a LAWNG function name */
2109 status
= purple_savedstatus_new(NULL
, newprim
);
2110 purple_savedstatus_set_message(status
, escnewmessage
);
2113 purple_savedstatus_activate(status
);
2116 gnt_box_give_focus_to_child(GNT_BOX(ggblist
->window
), ggblist
->tree
);
2118 g_free(escnewmessage
);
2119 if (ggblist
->typing
)
2120 g_source_remove(ggblist
->typing
);
2121 ggblist
->typing
= 0;
2126 status_selection_changed(GntComboBox
*box
, StatusBoxItem
*old
, StatusBoxItem
*now
, gpointer null
)
2128 gnt_entry_set_text(GNT_ENTRY(ggblist
->statustext
), NULL
);
2129 if (now
->type
== STATUS_SAVED_POPULAR
)
2131 /* Set the status immediately */
2132 purple_savedstatus_activate(now
->u
.saved
);
2134 else if (now
->type
== STATUS_PRIMITIVE
)
2136 /* Move the focus to the entry box */
2137 /* XXX: Make sure the selected status can have a message */
2138 gnt_box_move_focus(GNT_BOX(ggblist
->window
), 1);
2139 ggblist
->typing
= g_timeout_add_seconds(TYPING_TIMEOUT_S
, (GSourceFunc
)remove_typing_cb
, NULL
);
2141 else if (now
->type
== STATUS_SAVED_ALL
)
2143 /* Restore the selection to reflect current status. */
2144 savedstatus_changed(purple_savedstatus_get_current(), NULL
);
2145 gnt_box_give_focus_to_child(GNT_BOX(ggblist
->window
), ggblist
->tree
);
2146 finch_savedstatus_show_all();
2148 else if (now
->type
== STATUS_SAVED_NEW
)
2150 savedstatus_changed(purple_savedstatus_get_current(), NULL
);
2151 gnt_box_give_focus_to_child(GNT_BOX(ggblist
->window
), ggblist
->tree
);
2152 finch_savedstatus_edit(NULL
);
2155 g_return_if_reached();
2159 status_text_changed(GntEntry
*entry
, const char *text
, gpointer null
)
2161 if ((text
[0] == 27 || (text
[0] == '\t' && text
[1] == '\0')) && ggblist
->typing
== 0)
2164 if (ggblist
->typing
)
2165 g_source_remove(ggblist
->typing
);
2166 ggblist
->typing
= 0;
2168 if (text
[0] == '\r' && text
[1] == 0)
2170 /* Set the status only after you press 'Enter' */
2171 remove_typing_cb(NULL
);
2175 ggblist
->typing
= g_timeout_add_seconds(TYPING_TIMEOUT_S
, (GSourceFunc
)remove_typing_cb
, NULL
);
2180 savedstatus_changed(PurpleSavedStatus
*now
, PurpleSavedStatus
*old
)
2183 PurpleStatusPrimitive prim
;
2184 const char *message
;
2185 gboolean found
= FALSE
, saved
= TRUE
;
2190 /* Block the signals we don't want to emit */
2191 g_signal_handlers_block_matched(ggblist
->status
, G_SIGNAL_MATCH_FUNC
,
2192 0, 0, NULL
, status_selection_changed
, NULL
);
2193 g_signal_handlers_block_matched(ggblist
->statustext
, G_SIGNAL_MATCH_FUNC
,
2194 0, 0, NULL
, status_text_changed
, NULL
);
2196 prim
= purple_savedstatus_get_primitive_type(now
);
2197 message
= purple_savedstatus_get_message(now
);
2199 /* Rebuild the status dropdown */
2200 populate_status_dropdown();
2203 list
= g_object_get_data(G_OBJECT(ggblist
->status
), "list of statuses");
2204 for (; list
; list
= list
->next
)
2206 StatusBoxItem
*item
= list
->data
;
2207 if ((saved
&& item
->type
!= STATUS_PRIMITIVE
&& item
->u
.saved
== now
) ||
2208 (!saved
&& item
->type
== STATUS_PRIMITIVE
&& item
->u
.prim
== prim
))
2210 char *mess
= purple_unescape_html(message
);
2211 gnt_combo_box_set_selected(GNT_COMBO_BOX(ggblist
->status
), item
);
2212 gnt_entry_set_text(GNT_ENTRY(ggblist
->statustext
), mess
);
2213 gnt_widget_draw(ggblist
->status
);
2224 g_signal_handlers_unblock_matched(ggblist
->status
, G_SIGNAL_MATCH_FUNC
,
2225 0, 0, NULL
, status_selection_changed
, NULL
);
2226 g_signal_handlers_unblock_matched(ggblist
->statustext
, G_SIGNAL_MATCH_FUNC
,
2227 0, 0, NULL
, status_text_changed
, NULL
);
2231 blist_node_compare_position(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
2233 while ((n1
= purple_blist_node_get_sibling_prev(n1
)) != NULL
)
2240 blist_node_compare_text(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
2242 const char *s1
, *s2
;
2246 if (G_OBJECT_TYPE(n1
) != G_OBJECT_TYPE(n2
))
2247 return blist_node_compare_position(n1
, n2
);
2249 if (PURPLE_IS_CHAT(n1
)) {
2250 s1
= purple_chat_get_name((PurpleChat
*)n1
);
2251 s2
= purple_chat_get_name((PurpleChat
*)n2
);
2252 } else if (PURPLE_IS_BUDDY(n1
)) {
2253 return purple_buddy_presence_compare(
2254 PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n1
))),
2255 PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n2
))));
2256 } else if (PURPLE_IS_CONTACT(n1
)) {
2257 s1
= purple_contact_get_alias((PurpleContact
*)n1
);
2258 s2
= purple_contact_get_alias((PurpleContact
*)n2
);
2260 return blist_node_compare_position(n1
, n2
);
2263 us1
= g_utf8_strup(s1
, -1);
2264 us2
= g_utf8_strup(s2
, -1);
2265 ret
= g_utf8_collate(us1
, us2
);
2273 blist_node_compare_status(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
2277 if (G_OBJECT_TYPE(n1
) != G_OBJECT_TYPE(n2
))
2278 return blist_node_compare_position(n1
, n2
);
2280 if (PURPLE_IS_CONTACT(n1
))
2281 n1
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n1
)));
2282 if (PURPLE_IS_CONTACT(n2
))
2283 n2
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n2
)));
2285 if (PURPLE_IS_BUDDY(n1
) && PURPLE_IS_BUDDY(n2
)) {
2286 ret
= purple_buddy_presence_compare(
2287 PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n1
))),
2288 PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n2
))));
2292 return blist_node_compare_position(n1
, n2
);
2295 /* Sort alphabetically if presence is not comparable */
2296 ret
= blist_node_compare_text(n1
, n2
);
2302 get_contact_log_size(PurpleBlistNode
*c
)
2305 PurpleBlistNode
*node
;
2307 for (node
= purple_blist_node_get_first_child(c
); node
; node
= purple_blist_node_get_sibling_next(node
)) {
2308 PurpleBuddy
*b
= (PurpleBuddy
*)node
;
2309 log
+= purple_log_get_total_size(PURPLE_LOG_IM
, purple_buddy_get_name(b
),
2310 purple_buddy_get_account(b
));
2317 blist_node_compare_log(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
2320 PurpleBuddy
*b1
, *b2
;
2322 if (G_OBJECT_TYPE(n1
) != G_OBJECT_TYPE(n2
))
2323 return blist_node_compare_position(n1
, n2
);
2325 if (PURPLE_IS_BUDDY(n1
)) {
2326 b1
= (PurpleBuddy
*)n1
;
2327 b2
= (PurpleBuddy
*)n2
;
2328 ret
= purple_log_get_total_size(PURPLE_LOG_IM
, purple_buddy_get_name(b2
), purple_buddy_get_account(b2
)) -
2329 purple_log_get_total_size(PURPLE_LOG_IM
, purple_buddy_get_name(b1
), purple_buddy_get_account(b1
));
2332 } else if (PURPLE_IS_CONTACT(n1
)) {
2333 ret
= get_contact_log_size(n2
) - get_contact_log_size(n1
);
2337 return blist_node_compare_position(n1
, n2
);
2340 ret
= blist_node_compare_text(n1
, n2
);
2345 plugin_action(GntMenuItem
*item
, gpointer data
)
2347 PurplePluginAction
*action
= data
;
2348 if (action
&& action
->callback
)
2349 action
->callback(action
);
2353 build_plugin_actions(GntMenuItem
*item
, PurplePlugin
*plugin
)
2355 GntWidget
*sub
= gnt_menu_new(GNT_MENU_POPUP
);
2356 PurplePluginActionsCb actions_cb
;
2358 GntMenuItem
*menuitem
;
2361 purple_plugin_info_get_actions_cb(purple_plugin_get_info(plugin
));
2363 gnt_menuitem_set_submenu(item
, GNT_MENU(sub
));
2364 for (actions
= actions_cb(plugin
); actions
;
2365 actions
= g_list_delete_link(actions
, actions
)) {
2366 if (actions
->data
) {
2367 PurplePluginAction
*action
= actions
->data
;
2368 action
->plugin
= plugin
;
2369 menuitem
= gnt_menuitem_new(action
->label
);
2370 gnt_menu_add_item(GNT_MENU(sub
), menuitem
);
2372 gnt_menuitem_set_callback(menuitem
, plugin_action
, action
);
2373 g_object_set_data_full(G_OBJECT(menuitem
), "plugin_action",
2374 action
, (GDestroyNotify
)purple_plugin_action_free
);
2380 protocol_action(GntMenuItem
*item
, gpointer data
)
2382 PurpleProtocolAction
*action
= data
;
2383 if (action
&& action
->callback
)
2384 action
->callback(action
);
2388 build_protocol_actions(GntMenuItem
*item
, PurpleProtocol
*protocol
,
2389 PurpleConnection
*gc
)
2391 GntWidget
*sub
= gnt_menu_new(GNT_MENU_POPUP
);
2393 GntMenuItem
*menuitem
;
2395 gnt_menuitem_set_submenu(item
, GNT_MENU(sub
));
2396 for (actions
= purple_protocol_client_iface_get_actions(protocol
, gc
); actions
;
2397 actions
= g_list_delete_link(actions
, actions
)) {
2398 if (actions
->data
) {
2399 PurpleProtocolAction
*action
= actions
->data
;
2400 action
->connection
= gc
;
2401 menuitem
= gnt_menuitem_new(action
->label
);
2402 gnt_menu_add_item(GNT_MENU(sub
), menuitem
);
2404 gnt_menuitem_set_callback(menuitem
, protocol_action
, action
);
2405 g_object_set_data_full(G_OBJECT(menuitem
), "protocol_action",
2406 action
, (GDestroyNotify
)purple_protocol_action_free
);
2412 buddy_recent_signed_on_off(gpointer data
)
2414 PurpleBlistNode
*node
= data
;
2415 FinchBlistNode
*fnode
= purple_blist_node_get_ui_data(node
);
2417 g_source_remove(fnode
->signed_timer
);
2418 fnode
->signed_timer
= 0;
2420 if (!ggblist
->manager
->can_add_node(node
)) {
2421 node_remove(purple_blist_get_default(), node
);
2423 update_node_display(node
, ggblist
);
2424 if (purple_blist_node_get_parent(node
) && PURPLE_IS_CONTACT(purple_blist_node_get_parent(node
)))
2425 update_node_display(purple_blist_node_get_parent(node
), ggblist
);
2428 g_object_unref(node
);
2433 buddy_signed_on_off_cb(gpointer data
)
2435 PurpleBlistNode
*node
= data
;
2436 FinchBlistNode
*fnode
= purple_blist_node_get_ui_data(node
);
2437 if (!ggblist
|| !fnode
)
2440 if (fnode
->signed_timer
)
2441 g_source_remove(fnode
->signed_timer
);
2444 fnode
->signed_timer
= g_timeout_add_seconds(6, (GSourceFunc
)buddy_recent_signed_on_off
, data
);
2445 update_node_display(node
, ggblist
);
2446 if (purple_blist_node_get_parent(node
) && PURPLE_IS_CONTACT(purple_blist_node_get_parent(node
)))
2447 update_node_display(purple_blist_node_get_parent(node
), ggblist
);
2452 buddy_signed_on_off(PurpleBuddy
* buddy
, gpointer null
)
2454 g_idle_add(buddy_signed_on_off_cb
, buddy
);
2458 reconstruct_plugins_menu(void)
2467 if (ggblist
->plugins
== NULL
)
2468 ggblist
->plugins
= gnt_menuitem_new(_("Plugins"));
2470 plg
= ggblist
->plugins
;
2471 sub
= gnt_menu_new(GNT_MENU_POPUP
);
2472 gnt_menuitem_set_submenu(plg
, GNT_MENU(sub
));
2474 for (iter
= purple_plugins_get_loaded(); iter
; iter
= iter
->next
) {
2475 PurplePlugin
*plugin
= iter
->data
;
2476 PurplePluginInfo
*info
= purple_plugin_get_info(plugin
);
2479 if (!purple_plugin_info_get_actions_cb(info
))
2482 item
= gnt_menuitem_new(_(gplugin_plugin_info_get_name(
2483 GPLUGIN_PLUGIN_INFO(info
))));
2484 gnt_menu_add_item(GNT_MENU(sub
), item
);
2485 build_plugin_actions(item
, plugin
);
2490 reconstruct_accounts_menu(void)
2493 GntMenuItem
*acc
, *item
;
2499 if (ggblist
->accounts
== NULL
)
2500 ggblist
->accounts
= gnt_menuitem_new(_("Accounts"));
2502 acc
= ggblist
->accounts
;
2503 sub
= gnt_menu_new(GNT_MENU_POPUP
);
2504 gnt_menuitem_set_submenu(acc
, GNT_MENU(sub
));
2506 for (iter
= purple_accounts_get_all_active(); iter
;
2507 iter
= g_list_delete_link(iter
, iter
)) {
2508 PurpleAccount
*account
= iter
->data
;
2509 PurpleConnection
*gc
= purple_account_get_connection(account
);
2510 PurpleProtocol
*protocol
;
2512 if (!gc
|| !PURPLE_CONNECTION_IS_CONNECTED(gc
))
2514 protocol
= purple_connection_get_protocol(gc
);
2516 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol
, CLIENT
, get_actions
)) {
2517 item
= gnt_menuitem_new(purple_account_get_username(account
));
2518 gnt_menu_add_item(GNT_MENU(sub
), item
);
2519 build_protocol_actions(item
, protocol
, gc
);
2525 reconstruct_grouping_menu(void)
2530 if (!ggblist
|| !ggblist
->grouping
)
2533 subsub
= gnt_menu_new(GNT_MENU_POPUP
);
2534 gnt_menuitem_set_submenu(ggblist
->grouping
, GNT_MENU(subsub
));
2536 for (iter
= managers
; iter
; iter
= iter
->next
) {
2538 FinchBlistManager
*manager
= iter
->data
;
2539 GntMenuItem
*item
= gnt_menuitem_new(_(manager
->name
));
2540 g_snprintf(menuid
, sizeof(menuid
), "grouping-%s", manager
->id
);
2541 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), menuid
);
2542 gnt_menu_add_item(GNT_MENU(subsub
), item
);
2543 g_object_set_data_full(G_OBJECT(item
), "grouping-id", g_strdup(manager
->id
), g_free
);
2544 gnt_menuitem_set_callback(item
, menu_group_set_cb
, NULL
);
2549 auto_join_chats(gpointer data
)
2551 PurpleBlistNode
*node
;
2552 PurpleConnection
*pc
= data
;
2553 PurpleAccount
*account
= purple_connection_get_account(pc
);
2555 for (node
= purple_blist_get_default_root(); node
;
2556 node
= purple_blist_node_next(node
, FALSE
)) {
2557 if (PURPLE_IS_CHAT(node
)) {
2558 PurpleChat
*chat
= (PurpleChat
*)node
;
2559 if (purple_chat_get_account(chat
) == account
&&
2560 purple_blist_node_get_bool(node
, "gnt-autojoin"))
2561 purple_serv_join_chat(purple_account_get_connection(account
), purple_chat_get_components(chat
));
2568 account_autojoin_cb(PurpleConnection
*gc
, gpointer null
)
2570 g_idle_add(auto_join_chats
, gc
);
2574 static void toggle_pref_cb(GntMenuItem
*item
, gpointer n
)
2576 purple_prefs_set_bool(n
, !purple_prefs_get_bool(n
));
2579 static void sort_blist_change_cb(GntMenuItem
*item
, gpointer n
)
2581 purple_prefs_set_string(PREF_ROOT
"/sort_type", n
);
2585 block_select_cb(gpointer data
, PurpleRequestFields
*fields
)
2587 PurpleAccount
*account
= purple_request_fields_get_account(fields
, "account");
2588 const char *name
= purple_request_fields_get_string(fields
, "screenname");
2589 if (account
&& name
&& *name
!= '\0') {
2590 if (GPOINTER_TO_INT(purple_request_fields_get_choice(fields
, "block")) == 1) {
2591 purple_account_privacy_deny(account
, name
);
2593 purple_account_privacy_allow(account
, name
);
2599 block_select(GntMenuItem
*item
, gpointer n
)
2601 PurpleRequestFields
*fields
;
2602 PurpleRequestFieldGroup
*group
;
2603 PurpleRequestField
*field
;
2605 fields
= purple_request_fields_new();
2607 group
= purple_request_field_group_new(NULL
);
2608 purple_request_fields_add_group(fields
, group
);
2610 field
= purple_request_field_string_new("screenname", _("Name"), NULL
, FALSE
);
2611 purple_request_field_set_type_hint(field
, "screenname");
2612 purple_request_field_set_required(field
, TRUE
);
2613 purple_request_field_group_add_field(group
, field
);
2615 field
= purple_request_field_account_new("account", _("Account"), NULL
);
2616 purple_request_field_set_type_hint(field
, "account");
2617 purple_request_field_set_visible(field
,
2618 (purple_connections_get_all() != NULL
&&
2619 purple_connections_get_all()->next
!= NULL
));
2620 purple_request_field_set_required(field
, TRUE
);
2621 purple_request_field_group_add_field(group
, field
);
2623 field
= purple_request_field_choice_new("block", _("Block/Unblock"), GINT_TO_POINTER(1));
2624 purple_request_field_choice_add(field
, _("Block"), GINT_TO_POINTER(1));
2625 purple_request_field_choice_add(field
, _("Unblock"), GINT_TO_POINTER(2));
2626 purple_request_field_group_add_field(group
, field
);
2628 purple_request_fields(
2629 purple_blist_get_default(), _("Block/Unblock"), NULL
,
2630 _("Please enter the username or alias of the person "
2631 "you would like to Block/Unblock."),
2632 fields
, _("OK"), G_CALLBACK(block_select_cb
), _("Cancel"), NULL
,
2636 /* send_im_select* -- Xerox */
2638 send_im_select_cb(gpointer data
, PurpleRequestFields
*fields
)
2640 PurpleAccount
*account
;
2641 const char *username
;
2642 PurpleIMConversation
*im
;
2644 account
= purple_request_fields_get_account(fields
, "account");
2645 username
= purple_request_fields_get_string(fields
, "screenname");
2647 im
= purple_im_conversation_new(account
, username
);
2648 purple_conversation_present(PURPLE_CONVERSATION(im
));
2652 send_im_select(GntMenuItem
*item
, gpointer n
)
2654 PurpleRequestFields
*fields
;
2655 PurpleRequestFieldGroup
*group
;
2656 PurpleRequestField
*field
;
2658 fields
= purple_request_fields_new();
2660 group
= purple_request_field_group_new(NULL
);
2661 purple_request_fields_add_group(fields
, group
);
2663 field
= purple_request_field_string_new("screenname", _("Name"), NULL
, FALSE
);
2664 purple_request_field_set_type_hint(field
, "screenname");
2665 purple_request_field_set_required(field
, TRUE
);
2666 purple_request_field_group_add_field(group
, field
);
2668 field
= purple_request_field_account_new("account", _("Account"), NULL
);
2669 purple_request_field_set_type_hint(field
, "account");
2670 purple_request_field_set_visible(field
,
2671 (purple_connections_get_all() != NULL
&&
2672 purple_connections_get_all()->next
!= NULL
));
2673 purple_request_field_set_required(field
, TRUE
);
2674 purple_request_field_group_add_field(group
, field
);
2676 purple_request_fields(
2677 purple_blist_get_default(), _("New Instant Message"), NULL
,
2678 _("Please enter the username or alias of the person "
2679 "you would like to IM."),
2680 fields
, _("OK"), G_CALLBACK(send_im_select_cb
), _("Cancel"),
2685 join_chat_select_cb(gpointer data
, PurpleRequestFields
*fields
)
2687 PurpleAccount
*account
;
2689 PurpleConnection
*gc
;
2691 GHashTable
*hash
= NULL
;
2692 PurpleChatConversation
*conv
;
2694 account
= purple_request_fields_get_account(fields
, "account");
2695 name
= purple_request_fields_get_string(fields
, "chat");
2697 if (!purple_account_is_connected(account
))
2700 gc
= purple_account_get_connection(account
);
2701 /* Create a new conversation now. This will give focus to the new window.
2702 * But it's necessary to pretend that we left the chat, because otherwise
2703 * a new conversation window will pop up when we finally join the chat. */
2704 if (!(conv
= purple_conversations_find_chat_with_account(name
, account
))) {
2705 conv
= purple_chat_conversation_new(account
, name
);
2706 purple_chat_conversation_leave(conv
);
2708 purple_conversation_present(PURPLE_CONVERSATION(conv
));
2711 chat
= purple_blist_find_chat(account
, name
);
2713 PurpleProtocol
*protocol
= purple_connection_get_protocol(gc
);
2714 hash
= purple_protocol_chat_iface_info_defaults(protocol
, gc
, name
);
2716 hash
= purple_chat_get_components(chat
);
2718 purple_serv_join_chat(gc
, hash
);
2719 if (chat
== NULL
&& hash
!= NULL
)
2720 g_hash_table_destroy(hash
);
2724 join_chat_select(GntMenuItem
*item
, gpointer n
)
2726 PurpleRequestFields
*fields
;
2727 PurpleRequestFieldGroup
*group
;
2728 PurpleRequestField
*field
;
2730 fields
= purple_request_fields_new();
2732 group
= purple_request_field_group_new(NULL
);
2733 purple_request_fields_add_group(fields
, group
);
2735 field
= purple_request_field_string_new("chat", _("Channel"), NULL
, FALSE
);
2736 purple_request_field_set_required(field
, TRUE
);
2737 purple_request_field_group_add_field(group
, field
);
2739 field
= purple_request_field_account_new("account", _("Account"), NULL
);
2740 purple_request_field_set_type_hint(field
, "account");
2741 purple_request_field_set_visible(field
,
2742 (purple_connections_get_all() != NULL
&&
2743 purple_connections_get_all()->next
!= NULL
));
2744 purple_request_field_set_required(field
, TRUE
);
2745 purple_request_field_group_add_field(group
, field
);
2747 purple_request_fields(
2748 purple_blist_get_default(), _("Join a Chat"), NULL
,
2749 _("Please enter the name of the chat you want to join."),
2750 fields
, _("Join"), G_CALLBACK(join_chat_select_cb
), _("Cancel"),
2755 view_log_select_cb(gpointer data
, PurpleRequestFields
*fields
)
2757 PurpleAccount
*account
;
2760 PurpleContact
*contact
;
2762 account
= purple_request_fields_get_account(fields
, "account");
2763 name
= purple_request_fields_get_string(fields
, "screenname");
2765 buddy
= purple_blist_find_buddy(account
, name
);
2767 contact
= purple_buddy_get_contact(buddy
);
2773 finch_log_show_contact(contact
);
2775 finch_log_show(PURPLE_LOG_IM
, name
, account
);
2780 view_log_cb(GntMenuItem
*item
, gpointer n
)
2782 PurpleRequestFields
*fields
;
2783 PurpleRequestFieldGroup
*group
;
2784 PurpleRequestField
*field
;
2786 fields
= purple_request_fields_new();
2788 group
= purple_request_field_group_new(NULL
);
2789 purple_request_fields_add_group(fields
, group
);
2791 field
= purple_request_field_string_new("screenname", _("Name"), NULL
, FALSE
);
2792 purple_request_field_set_type_hint(field
, "screenname-all");
2793 purple_request_field_set_required(field
, TRUE
);
2794 purple_request_field_group_add_field(group
, field
);
2796 field
= purple_request_field_account_new("account", _("Account"), NULL
);
2797 purple_request_field_set_type_hint(field
, "account");
2798 purple_request_field_set_visible(field
,
2799 (purple_accounts_get_all() != NULL
&&
2800 purple_accounts_get_all()->next
!= NULL
));
2801 purple_request_field_set_required(field
, TRUE
);
2802 purple_request_field_group_add_field(group
, field
);
2803 purple_request_field_account_set_show_all(field
, TRUE
);
2805 purple_request_fields(
2806 purple_blist_get_default(), _("View Log"), NULL
,
2807 _("Please enter the username or alias of the person "
2808 "whose log you would like to view."),
2809 fields
, _("OK"), G_CALLBACK(view_log_select_cb
), _("Cancel"),
2814 view_all_logs_cb(GntMenuItem
*item
, gpointer n
)
2816 finch_log_show(PURPLE_LOG_IM
, NULL
, NULL
);
2820 menu_add_buddy_cb(GntMenuItem
*item
, gpointer null
)
2822 purple_blist_request_add_buddy(NULL
, NULL
, NULL
, NULL
);
2826 menu_add_chat_cb(GntMenuItem
*item
, gpointer null
)
2828 purple_blist_request_add_chat(NULL
, NULL
, NULL
, NULL
);
2832 menu_add_group_cb(GntMenuItem
*item
, gpointer null
)
2834 purple_blist_request_add_group();
2838 menu_group_set_cb(GntMenuItem
*item
, gpointer null
)
2840 const char *id
= g_object_get_data(G_OBJECT(item
), "grouping-id");
2841 purple_prefs_set_string(PREF_ROOT
"/grouping", id
);
2847 GntWidget
*menu
, *sub
, *subsub
;
2854 window
= GNT_WINDOW(ggblist
->window
);
2855 ggblist
->menu
= menu
= gnt_menu_new(GNT_MENU_TOPLEVEL
);
2856 gnt_window_set_menu(window
, GNT_MENU(menu
));
2858 item
= gnt_menuitem_new(_("Options"));
2859 gnt_menu_add_item(GNT_MENU(menu
), item
);
2861 sub
= gnt_menu_new(GNT_MENU_POPUP
);
2862 gnt_menuitem_set_submenu(item
, GNT_MENU(sub
));
2864 item
= gnt_menuitem_new(_("Send IM..."));
2865 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "send-im");
2866 gnt_menu_add_item(GNT_MENU(sub
), item
);
2867 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), send_im_select
, NULL
);
2869 item
= gnt_menuitem_new(_("Block/Unblock..."));
2870 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "block-unblock");
2871 gnt_menu_add_item(GNT_MENU(sub
), item
);
2872 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), block_select
, NULL
);
2874 item
= gnt_menuitem_new(_("Join Chat..."));
2875 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "join-chat");
2876 gnt_menu_add_item(GNT_MENU(sub
), item
);
2877 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), join_chat_select
, NULL
);
2879 item
= gnt_menuitem_new(_("View Log..."));
2880 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "view-log");
2881 gnt_menu_add_item(GNT_MENU(sub
), item
);
2882 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), view_log_cb
, NULL
);
2884 item
= gnt_menuitem_new(_("View All Logs"));
2885 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "view-all-logs");
2886 gnt_menu_add_item(GNT_MENU(sub
), item
);
2887 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), view_all_logs_cb
, NULL
);
2889 item
= gnt_menuitem_new(_("Show"));
2890 gnt_menu_add_item(GNT_MENU(sub
), item
);
2891 subsub
= gnt_menu_new(GNT_MENU_POPUP
);
2892 gnt_menuitem_set_submenu(item
, GNT_MENU(subsub
));
2894 item
= gnt_menuitem_check_new(_("Empty groups"));
2895 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "show-empty-groups");
2896 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item
),
2897 purple_prefs_get_bool(PREF_ROOT
"/emptygroups"));
2898 gnt_menu_add_item(GNT_MENU(subsub
), item
);
2899 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), toggle_pref_cb
, PREF_ROOT
"/emptygroups");
2901 item
= gnt_menuitem_check_new(_("Offline buddies"));
2902 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "show-offline-buddies");
2903 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item
),
2904 purple_prefs_get_bool(PREF_ROOT
"/showoffline"));
2905 gnt_menu_add_item(GNT_MENU(subsub
), item
);
2906 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), toggle_pref_cb
, PREF_ROOT
"/showoffline");
2908 item
= gnt_menuitem_new(_("Sort"));
2909 gnt_menu_add_item(GNT_MENU(sub
), item
);
2910 subsub
= gnt_menu_new(GNT_MENU_POPUP
);
2911 gnt_menuitem_set_submenu(item
, GNT_MENU(subsub
));
2913 item
= gnt_menuitem_new(_("By Status"));
2914 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "sort-status");
2915 gnt_menu_add_item(GNT_MENU(subsub
), item
);
2916 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), sort_blist_change_cb
, "status");
2918 item
= gnt_menuitem_new(_("Alphabetically"));
2919 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "sort-alpha");
2920 gnt_menu_add_item(GNT_MENU(subsub
), item
);
2921 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), sort_blist_change_cb
, "text");
2923 item
= gnt_menuitem_new(_("By Log Size"));
2924 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "sort-log");
2925 gnt_menu_add_item(GNT_MENU(subsub
), item
);
2926 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), sort_blist_change_cb
, "log");
2928 item
= gnt_menuitem_new(_("Add"));
2929 gnt_menu_add_item(GNT_MENU(sub
), item
);
2931 subsub
= gnt_menu_new(GNT_MENU_POPUP
);
2932 gnt_menuitem_set_submenu(item
, GNT_MENU(subsub
));
2934 item
= gnt_menuitem_new(_("Buddy"));
2935 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "add-buddy");
2936 gnt_menu_add_item(GNT_MENU(subsub
), item
);
2937 gnt_menuitem_set_callback(item
, menu_add_buddy_cb
, NULL
);
2939 item
= gnt_menuitem_new(_("Chat"));
2940 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "add-chat");
2941 gnt_menu_add_item(GNT_MENU(subsub
), item
);
2942 gnt_menuitem_set_callback(item
, menu_add_chat_cb
, NULL
);
2944 item
= gnt_menuitem_new(_("Group"));
2945 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "add-group");
2946 gnt_menu_add_item(GNT_MENU(subsub
), item
);
2947 gnt_menuitem_set_callback(item
, menu_add_group_cb
, NULL
);
2949 ggblist
->grouping
= item
= gnt_menuitem_new(_("Grouping"));
2950 gnt_menu_add_item(GNT_MENU(sub
), item
);
2951 reconstruct_grouping_menu();
2953 reconstruct_accounts_menu();
2954 gnt_menu_add_item(GNT_MENU(menu
), ggblist
->accounts
);
2956 reconstruct_plugins_menu();
2957 gnt_menu_add_item(GNT_MENU(menu
), ggblist
->plugins
);
2960 void finch_blist_show()
2962 blist_show(purple_blist_get_default());
2966 group_collapsed(GntWidget
*widget
, PurpleBlistNode
*node
, gboolean collapsed
, gpointer null
)
2968 if (PURPLE_IS_GROUP(node
))
2969 purple_blist_node_set_bool(node
, "collapsed", collapsed
);
2973 blist_show(PurpleBuddyList
*list
)
2975 if (ggblist
->window
) {
2976 gnt_window_present(ggblist
->window
);
2980 ggblist
->window
= gnt_vwindow_new(FALSE
);
2981 gnt_widget_set_name(ggblist
->window
, "buddylist");
2982 gnt_box_set_toplevel(GNT_BOX(ggblist
->window
), TRUE
);
2983 gnt_box_set_title(GNT_BOX(ggblist
->window
), _("Buddy List"));
2984 gnt_box_set_pad(GNT_BOX(ggblist
->window
), 0);
2986 ggblist
->tree
= gnt_tree_new();
2988 gnt_widget_set_has_border(ggblist
->tree
, FALSE
);
2989 gnt_widget_set_size(ggblist
->tree
, purple_prefs_get_int(PREF_ROOT
"/size/width"),
2990 purple_prefs_get_int(PREF_ROOT
"/size/height"));
2991 gnt_widget_set_position(ggblist
->window
, purple_prefs_get_int(PREF_ROOT
"/position/x"),
2992 purple_prefs_get_int(PREF_ROOT
"/position/y"));
2994 gnt_box_add_widget(GNT_BOX(ggblist
->window
), ggblist
->tree
);
2996 ggblist
->status
= gnt_combo_box_new();
2997 gnt_box_add_widget(GNT_BOX(ggblist
->window
), ggblist
->status
);
2998 ggblist
->statustext
= gnt_entry_new(NULL
);
2999 gnt_box_add_widget(GNT_BOX(ggblist
->window
), ggblist
->statustext
);
3001 gnt_widget_show(ggblist
->window
);
3003 purple_signal_connect(purple_connections_get_handle(), "signed-on", finch_blist_get_handle(),
3004 PURPLE_CALLBACK(reconstruct_accounts_menu
), NULL
);
3005 purple_signal_connect(purple_connections_get_handle(), "signed-off", finch_blist_get_handle(),
3006 PURPLE_CALLBACK(reconstruct_accounts_menu
), NULL
);
3007 purple_signal_connect(purple_accounts_get_handle(), "account-actions-changed", finch_blist_get_handle(),
3008 PURPLE_CALLBACK(reconstruct_accounts_menu
), NULL
);
3009 purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", finch_blist_get_handle(),
3010 PURPLE_CALLBACK(buddy_status_changed
), ggblist
);
3011 purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", finch_blist_get_handle(),
3012 PURPLE_CALLBACK(buddy_idle_changed
), ggblist
);
3014 purple_signal_connect(purple_plugins_get_handle(), "plugin-load", finch_blist_get_handle(),
3015 PURPLE_CALLBACK(reconstruct_plugins_menu
), NULL
);
3016 purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", finch_blist_get_handle(),
3017 PURPLE_CALLBACK(reconstruct_plugins_menu
), NULL
);
3019 purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_blist_get_handle(),
3020 PURPLE_CALLBACK(buddy_signed_on_off
), ggblist
);
3021 purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_blist_get_handle(),
3022 PURPLE_CALLBACK(buddy_signed_on_off
), ggblist
);
3024 g_signal_connect(G_OBJECT(ggblist
->tree
), "selection_changed", G_CALLBACK(selection_changed
), ggblist
);
3025 g_signal_connect(G_OBJECT(ggblist
->tree
), "key_pressed", G_CALLBACK(key_pressed
), ggblist
);
3026 g_signal_connect(G_OBJECT(ggblist
->tree
), "context-menu", G_CALLBACK(context_menu
), ggblist
);
3027 g_signal_connect(G_OBJECT(ggblist
->tree
), "collapse-toggled", G_CALLBACK(group_collapsed
), NULL
);
3028 g_signal_connect(G_OBJECT(ggblist
->tree
), "activate", G_CALLBACK(selection_activate
), ggblist
);
3029 g_signal_connect_data(G_OBJECT(ggblist
->tree
), "gained-focus", G_CALLBACK(draw_tooltip
),
3030 ggblist
, 0, G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
3031 g_signal_connect_data(G_OBJECT(ggblist
->tree
), "lost-focus", G_CALLBACK(remove_peripherals
),
3032 ggblist
, 0, G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
3033 g_signal_connect_data(G_OBJECT(ggblist
->window
), "workspace-hidden", G_CALLBACK(remove_peripherals
),
3034 ggblist
, 0, G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
3035 g_signal_connect(G_OBJECT(ggblist
->tree
), "size_changed", G_CALLBACK(size_changed_cb
), NULL
);
3036 g_signal_connect(G_OBJECT(ggblist
->window
), "position_set", G_CALLBACK(save_position_cb
), NULL
);
3037 g_signal_connect(G_OBJECT(ggblist
->window
), "destroy", G_CALLBACK(reset_blist_window
), NULL
);
3039 /* Status signals */
3040 purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-changed", finch_blist_get_handle(),
3041 PURPLE_CALLBACK(savedstatus_changed
), NULL
);
3042 g_signal_connect(G_OBJECT(ggblist
->status
), "selection_changed",
3043 G_CALLBACK(status_selection_changed
), NULL
);
3044 g_signal_connect(G_OBJECT(ggblist
->statustext
), "key_pressed",
3045 G_CALLBACK(status_text_changed
), NULL
);
3049 populate_buddylist();
3051 savedstatus_changed(purple_savedstatus_get_current(), NULL
);
3054 void finch_blist_uninit()
3058 gboolean
finch_blist_get_position(int *x
, int *y
)
3060 if (!ggblist
|| !ggblist
->window
)
3062 gnt_widget_get_position(ggblist
->window
, x
, y
);
3066 void finch_blist_set_position(int x
, int y
)
3068 gnt_widget_set_position(ggblist
->window
, x
, y
);
3071 gboolean
finch_blist_get_size(int *width
, int *height
)
3073 if (!ggblist
|| !ggblist
->window
)
3075 gnt_widget_get_size(ggblist
->window
, width
, height
);
3079 void finch_blist_set_size(int width
, int height
)
3081 gnt_widget_set_size(ggblist
->window
, width
, height
);
3084 void finch_blist_install_manager(const FinchBlistManager
*manager
)
3086 if (!g_list_find(managers
, manager
)) {
3087 managers
= g_list_append(managers
, (gpointer
)manager
);
3088 reconstruct_grouping_menu();
3089 if (purple_strequal(manager
->id
, purple_prefs_get_string(PREF_ROOT
"/grouping")))
3090 purple_prefs_trigger_callback(PREF_ROOT
"/grouping");
3094 void finch_blist_uninstall_manager(const FinchBlistManager
*manager
)
3096 if (g_list_find(managers
, manager
)) {
3097 managers
= g_list_remove(managers
, manager
);
3098 reconstruct_grouping_menu();
3099 if (purple_strequal(manager
->id
, purple_prefs_get_string(PREF_ROOT
"/grouping")))
3100 purple_prefs_trigger_callback(PREF_ROOT
"/grouping");
3104 FinchBlistManager
* finch_blist_manager_find(const char *id
)
3106 GList
*iter
= managers
;
3110 for (; iter
; iter
= iter
->next
) {
3111 FinchBlistManager
*m
= iter
->data
;
3112 if (purple_strequal(id
, m
->id
))
3118 GntTree
* finch_blist_get_tree(void)
3120 return ggblist
? GNT_TREE(ggblist
->tree
) : NULL
;
3123 /**************************************************************************
3125 **************************************************************************/
3126 G_DEFINE_TYPE(FinchBuddyList
, finch_buddy_list
, PURPLE_TYPE_BUDDY_LIST
)
3129 finch_buddy_list_init(FinchBuddyList
*self
)
3132 /* The first buddy list object becomes the default. */
3136 self
->manager
= finch_blist_manager_find(
3137 purple_prefs_get_string(PREF_ROOT
"/grouping"));
3138 if (!self
->manager
) {
3139 self
->manager
= &default_manager
;
3144 finch_buddy_list_finalize(GObject
*obj
)
3146 FinchBuddyList
*ggblist
= FINCH_BUDDY_LIST(obj
);
3148 gnt_widget_destroy(ggblist
->window
);
3150 G_OBJECT_CLASS(finch_buddy_list_parent_class
)->finalize(obj
);
3154 finch_buddy_list_class_init(FinchBuddyListClass
*klass
)
3156 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
3157 PurpleBuddyListClass
*purple_blist_class
;
3159 obj_class
->finalize
= finch_buddy_list_finalize
;
3161 purple_blist_class
= PURPLE_BUDDY_LIST_CLASS(klass
);
3162 purple_blist_class
->new_node
= new_node
;
3163 purple_blist_class
->show
= blist_show
;
3164 purple_blist_class
->update
= node_update
;
3165 purple_blist_class
->remove
= node_remove
;
3166 purple_blist_class
->request_add_buddy
= finch_request_add_buddy
;
3167 purple_blist_class
->request_add_chat
= finch_request_add_chat
;
3168 purple_blist_class
->request_add_group
= finch_request_add_group
;
3171 /**************************************************************************
3173 **************************************************************************/
3174 static FinchBlistManager
*
3175 finch_blist_manager_copy(FinchBlistManager
*manager
)
3177 FinchBlistManager
*manager_new
;
3179 g_return_val_if_fail(manager
!= NULL
, NULL
);
3181 manager_new
= g_new(FinchBlistManager
, 1);
3182 *manager_new
= *manager
;
3188 finch_blist_manager_free(FinchBlistManager
*manager
)
3190 g_return_if_fail(manager
!= NULL
);
3196 finch_blist_manager_get_type(void)
3198 static GType type
= 0;
3201 type
= g_boxed_type_register_static("FinchBlistManager",
3202 (GBoxedCopyFunc
)finch_blist_manager_copy
,
3203 (GBoxedFreeFunc
)finch_blist_manager_free
);