2 * @file gntblist.c GNT BuddyList API
8 * Finch is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
34 #include <savedstatuses.h>
42 #include "gntcolors.h"
43 #include "gntcombobox.h"
50 #include "gntmenuitem.h"
51 #include "gntmenuitemcheck.h"
52 #include "gntpounce.h"
56 #include "gntwindow.h"
60 #include "gntstatus.h"
63 #define PREF_ROOT "/finch/blist"
64 #define TYPING_TIMEOUT_S 4
66 #define SHOW_EMPTY_GROUP_TIMEOUT 60
74 PurpleBlistNode
*tnode
; /* Who is the tooltip being displayed for? */
75 GList
*tagged
; /* A list of tagged blistnodes */
78 PurpleBlistNode
*cnode
;
80 /* XXX: I am KISSing */
81 GntWidget
*status
; /* Dropdown with the statuses */
82 GntWidget
*statustext
; /* Status message */
86 /* These are the menuitems that get regenerated */
87 GntMenuItem
*accounts
;
89 GntMenuItem
*grouping
;
91 /* When a new group is manually added, it is empty, but we still want to show it
92 * for a while (SHOW_EMPTY_GROUP_TIMEOUT seconds) even if 'show empty groups' is
96 guint new_group_timeout
;
98 FinchBlistManager
*manager
;
103 gpointer row
; /* the row in the GntTree */
104 guint signed_timer
; /* used when 'recently' signed on/off */
109 STATUS_PRIMITIVE
= 0,
110 STATUS_SAVED_POPULAR
,
120 PurpleStatusPrimitive prim
;
121 PurpleSavedStatus
*saved
;
125 static FinchBlist
*ggblist
;
127 static void add_buddy(PurpleBuddy
*buddy
, FinchBlist
*ggblist
);
128 static void add_contact(PurpleContact
*contact
, FinchBlist
*ggblist
);
129 static void add_group(PurpleGroup
*group
, FinchBlist
*ggblist
);
130 static void add_chat(PurpleChat
*chat
, FinchBlist
*ggblist
);
131 static void add_node(PurpleBlistNode
*node
, FinchBlist
*ggblist
);
132 static void node_update(PurpleBuddyList
*list
, PurpleBlistNode
*node
);
134 static gboolean
is_contact_online(PurpleContact
*contact
);
135 static gboolean
is_group_online(PurpleGroup
*group
);
137 static void draw_tooltip(FinchBlist
*ggblist
);
138 static void tooltip_for_buddy(PurpleBuddy
*buddy
, GString
*str
, gboolean full
);
139 static gboolean
remove_typing_cb(gpointer null
);
140 static void remove_peripherals(FinchBlist
*ggblist
);
141 static const char * get_display_name(PurpleBlistNode
*node
);
142 static void savedstatus_changed(PurpleSavedStatus
*now
, PurpleSavedStatus
*old
);
143 static void blist_show(PurpleBuddyList
*list
);
144 static void update_node_display(PurpleBlistNode
*buddy
, FinchBlist
*ggblist
);
145 static void update_buddy_display(PurpleBuddy
*buddy
, FinchBlist
*ggblist
);
146 static void account_signed_on_cb(PurpleConnection
*pc
, gpointer null
);
147 static void finch_request_add_buddy(PurpleAccount
*account
, const char *username
, const char *grp
, const char *alias
);
148 static void menu_group_set_cb(GntMenuItem
*item
, gpointer null
);
151 static int blist_node_compare_position(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
152 static int blist_node_compare_text(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
153 static int blist_node_compare_status(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
154 static int blist_node_compare_log(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
156 static int color_available
;
157 static int color_away
;
158 static int color_offline
;
159 static int color_idle
;
162 * Buddy List Manager functions.
165 static gboolean
default_can_add_node(PurpleBlistNode
*node
)
167 gboolean offline
= purple_prefs_get_bool(PREF_ROOT
"/showoffline");
169 if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
170 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
171 FinchBlistNode
*fnode
= FINCH_GET_DATA(node
);
172 if (!purple_buddy_get_contact(buddy
))
173 return FALSE
; /* When a new buddy is added and show-offline is set */
174 if (PURPLE_BUDDY_IS_ONLINE(buddy
))
175 return TRUE
; /* The buddy is online */
176 if (!purple_account_is_connected(purple_buddy_get_account(buddy
)))
177 return FALSE
; /* The account is disconnected. Do not show */
179 return TRUE
; /* We want to see offline buddies too */
180 if (fnode
&& fnode
->signed_timer
)
181 return TRUE
; /* Show if the buddy just signed off */
182 if (purple_blist_node_get_bool(node
, "show_offline"))
184 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
186 for (nd
= purple_blist_node_get_first_child(node
);
187 nd
; nd
= purple_blist_node_get_sibling_next(nd
)) {
188 if (default_can_add_node(nd
))
191 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
192 PurpleChat
*chat
= (PurpleChat
*)node
;
193 if (purple_account_is_connected(purple_chat_get_account(chat
)))
194 return TRUE
; /* Show whenever the account is online */
195 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
197 gboolean empty
= purple_prefs_get_bool(PREF_ROOT
"/emptygroups");
199 return TRUE
; /* If we want to see empty groups, we can show any group */
201 for (nd
= purple_blist_node_get_first_child(node
);
202 nd
; nd
= purple_blist_node_get_sibling_next(nd
)) {
203 if (default_can_add_node(nd
))
207 if (ggblist
&& ggblist
->new_group
&& g_list_find(ggblist
->new_group
, node
))
214 static gpointer
default_find_parent(PurpleBlistNode
*node
)
217 switch (purple_blist_node_get_type(node
)) {
218 case PURPLE_BLIST_BUDDY_NODE
:
219 case PURPLE_BLIST_CONTACT_NODE
:
220 case PURPLE_BLIST_CHAT_NODE
:
221 ret
= purple_blist_node_get_parent(node
);
227 add_node(ret
, ggblist
);
231 static gboolean
default_create_tooltip(gpointer selected_row
, GString
**body
, char **tool_title
)
234 PurpleBlistNode
*node
= selected_row
;
239 purple_blist_node_get_type(node
) == PURPLE_BLIST_OTHER_NODE
)
242 str
= g_string_new("");
244 if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
245 PurpleBuddy
*pr
= purple_contact_get_priority_buddy((PurpleContact
*)node
);
246 gboolean offline
= !PURPLE_BUDDY_IS_ONLINE(pr
);
247 gboolean showoffline
= purple_prefs_get_bool(PREF_ROOT
"/showoffline");
248 const char *name
= purple_buddy_get_name(pr
);
250 title
= g_strdup(name
);
251 tooltip_for_buddy(pr
, str
, TRUE
);
252 for (node
= purple_blist_node_get_first_child(node
); node
; node
= purple_blist_node_get_sibling_next(node
)) {
253 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
255 int value
= purple_blist_node_get_int(node
, "last_seen");
256 if (value
> lastseen
)
259 if (node
== (PurpleBlistNode
*)pr
)
261 if (!purple_account_is_connected(purple_buddy_get_account(buddy
)))
263 if (!showoffline
&& !PURPLE_BUDDY_IS_ONLINE(buddy
))
265 str
= g_string_append(str
, "\n----------\n");
266 tooltip_for_buddy(buddy
, str
, FALSE
);
268 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
269 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
270 tooltip_for_buddy(buddy
, str
, TRUE
);
271 title
= g_strdup(purple_buddy_get_name(buddy
));
272 if (!PURPLE_BUDDY_IS_ONLINE((PurpleBuddy
*)node
))
273 lastseen
= purple_blist_node_get_int(node
, "last_seen");
274 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
275 PurpleGroup
*group
= (PurpleGroup
*)node
;
277 g_string_append_printf(str
, _("Online: %d\nTotal: %d"),
278 purple_blist_get_group_online_count(group
),
279 purple_blist_get_group_size(group
, FALSE
));
281 title
= g_strdup(purple_group_get_name(group
));
282 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
283 PurpleChat
*chat
= (PurpleChat
*)node
;
284 PurpleAccount
*account
= purple_chat_get_account(chat
);
286 g_string_append_printf(str
, _("Account: %s (%s)"),
287 purple_account_get_username(account
),
288 purple_account_get_protocol_name(account
));
290 title
= g_strdup(purple_chat_get_name(chat
));
292 g_string_free(str
, TRUE
);
297 char *tmp
= purple_str_seconds_to_string(time(NULL
) - lastseen
);
298 g_string_append_printf(str
, _("\nLast Seen: %s ago"), tmp
);
310 g_string_free(str
, TRUE
);
315 static FinchBlistManager default_manager
=
321 default_can_add_node
,
323 default_create_tooltip
,
324 {NULL
, NULL
, NULL
, NULL
}
326 static GList
*managers
;
328 static FinchBlistNode
*
329 create_finch_blist_node(PurpleBlistNode
*node
, gpointer row
)
331 FinchBlistNode
*fnode
= FINCH_GET_DATA(node
);
333 fnode
= g_new0(FinchBlistNode
, 1);
334 fnode
->signed_timer
= 0;
335 FINCH_SET_DATA(node
, fnode
);
342 reset_blist_node_ui_data(PurpleBlistNode
*node
)
344 FinchBlistNode
*fnode
= FINCH_GET_DATA(node
);
347 if (fnode
->signed_timer
)
348 purple_timeout_remove(fnode
->signed_timer
);
350 FINCH_SET_DATA(node
, NULL
);
354 get_display_color(PurpleBlistNode
*node
)
359 if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
360 node
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
)));
361 if (!PURPLE_BLIST_NODE_IS_BUDDY(node
))
364 buddy
= (PurpleBuddy
*)node
;
365 if (purple_presence_is_idle(purple_buddy_get_presence(buddy
))) {
367 } else if (purple_presence_is_available(purple_buddy_get_presence(buddy
))) {
368 color
= color_available
;
369 } else if (purple_presence_is_online(purple_buddy_get_presence(buddy
)) &&
370 !purple_presence_is_available(purple_buddy_get_presence(buddy
))) {
372 } else if (!purple_presence_is_online(purple_buddy_get_presence(buddy
))) {
373 color
= color_offline
;
379 static GntTextFormatFlags
380 get_blist_node_flag(PurpleBlistNode
*node
)
382 GntTextFormatFlags flag
= 0;
383 FinchBlistNode
*fnode
= FINCH_GET_DATA(node
);
385 if (ggblist
->tagged
&& g_list_find(ggblist
->tagged
, node
))
386 flag
|= GNT_TEXT_FLAG_BOLD
;
388 if (fnode
&& fnode
->signed_timer
)
389 flag
|= GNT_TEXT_FLAG_BLINK
;
390 else if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
391 node
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
)));
392 fnode
= FINCH_GET_DATA(node
);
393 if (fnode
&& fnode
->signed_timer
)
394 flag
|= GNT_TEXT_FLAG_BLINK
;
395 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
396 /* If the node is collapsed, then check to see if any of the priority buddies of
397 * any of the contacts within this group recently signed on/off, and set the blink
398 * flag appropriately. */
399 /* XXX: Refs #5444 */
400 /* XXX: there's no way I can ask if the node is expanded or not? *sigh*
401 * API addition would be necessary */
403 if (!gnt_tree_get_expanded(GNT_TREE(ggblist
->tree
), node
)) {
404 for (node
= purple_blist_node_get_first_child(node
); node
;
405 node
= purple_blist_node_get_sibling_next(node
)) {
406 PurpleBlistNode
*pnode
;
407 pnode
= purple_contact_get_priority_buddy((PurpleContact
*)node
);
408 fnode
= FINCH_GET_DATA(node
);
409 if (fnode
&& fnode
->signed_timer
) {
410 flag
|= GNT_TEXT_FLAG_BLINK
;
422 blist_update_row_flags(PurpleBlistNode
*node
)
424 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), node
, get_blist_node_flag(node
));
425 gnt_tree_set_row_color(GNT_TREE(ggblist
->tree
), node
, get_display_color(node
));
430 is_contact_online(PurpleContact
*contact
)
432 PurpleBlistNode
*node
;
433 for (node
= purple_blist_node_get_first_child(((PurpleBlistNode
*)contact
)); node
;
434 node
= purple_blist_node_get_sibling_next(node
)) {
435 FinchBlistNode
*fnode
= FINCH_GET_DATA(node
);
436 if (PURPLE_BUDDY_IS_ONLINE((PurpleBuddy
*)node
) ||
437 (fnode
&& fnode
->signed_timer
))
444 is_group_online(PurpleGroup
*group
)
446 PurpleBlistNode
*node
;
447 for (node
= purple_blist_node_get_first_child(((PurpleBlistNode
*)group
)); node
;
448 node
= purple_blist_node_get_sibling_next(node
)) {
449 if (PURPLE_BLIST_NODE_IS_CHAT(node
) &&
450 purple_account_is_connected(((PurpleChat
*)node
)->account
))
452 else if (is_contact_online((PurpleContact
*)node
))
460 new_node(PurpleBlistNode
*node
)
465 add_node(PurpleBlistNode
*node
, FinchBlist
*ggblist
)
467 if (FINCH_GET_DATA(node
))
470 if (!ggblist
->manager
->can_add_node(node
))
473 if (PURPLE_BLIST_NODE_IS_BUDDY(node
))
474 add_buddy((PurpleBuddy
*)node
, ggblist
);
475 else if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
476 add_contact((PurpleContact
*)node
, ggblist
);
477 else if (PURPLE_BLIST_NODE_IS_GROUP(node
))
478 add_group((PurpleGroup
*)node
, ggblist
);
479 else if (PURPLE_BLIST_NODE_IS_CHAT(node
))
480 add_chat((PurpleChat
*)node
, ggblist
);
482 draw_tooltip(ggblist
);
485 void finch_blist_manager_add_node(PurpleBlistNode
*node
)
487 add_node(node
, ggblist
);
491 remove_tooltip(FinchBlist
*ggblist
)
493 gnt_widget_destroy(ggblist
->tooltip
);
494 ggblist
->tooltip
= NULL
;
495 ggblist
->tnode
= NULL
;
499 node_remove(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
501 FinchBlist
*ggblist
= FINCH_GET_DATA(list
);
502 PurpleBlistNode
*parent
;
504 if (ggblist
== NULL
|| FINCH_GET_DATA(node
) == NULL
)
507 if (PURPLE_BLIST_NODE_IS_GROUP(node
) && ggblist
->new_group
) {
508 ggblist
->new_group
= g_list_remove(ggblist
->new_group
, node
);
511 gnt_tree_remove(GNT_TREE(ggblist
->tree
), node
);
512 reset_blist_node_ui_data(node
);
514 ggblist
->tagged
= g_list_remove(ggblist
->tagged
, node
);
516 parent
= purple_blist_node_get_parent(node
);
517 for (node
= purple_blist_node_get_first_child(node
); node
;
518 node
= purple_blist_node_get_sibling_next(node
))
519 node_remove(list
, node
);
522 if (!ggblist
->manager
->can_add_node(parent
))
523 node_remove(list
, parent
);
525 node_update(list
, parent
);
528 draw_tooltip(ggblist
);
532 node_update(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
534 /* It really looks like this should never happen ... but it does.
535 This will at least emit a warning to the log when it
536 happens, so maybe someone will figure it out. */
537 g_return_if_fail(node
!= NULL
);
539 if (FINCH_GET_DATA(list
)== NULL
)
540 return; /* XXX: this is probably the place to auto-join chats */
542 if (ggblist
->window
== NULL
)
545 if (FINCH_GET_DATA(node
)!= NULL
) {
546 gnt_tree_change_text(GNT_TREE(ggblist
->tree
), node
,
547 0, get_display_name(node
));
548 gnt_tree_sort_row(GNT_TREE(ggblist
->tree
), node
);
549 blist_update_row_flags(node
);
550 if (gnt_tree_get_parent_key(GNT_TREE(ggblist
->tree
), node
) !=
551 ggblist
->manager
->find_parent(node
))
552 node_remove(list
, node
);
555 if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
556 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
557 add_node((PurpleBlistNode
*)buddy
, FINCH_GET_DATA(list
));
558 node_update(list
, purple_blist_node_get_parent(node
));
559 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
560 add_node(node
, FINCH_GET_DATA(list
));
561 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
562 if (FINCH_GET_DATA(node
)== NULL
) {
563 /* The core seems to expect the UI to add the buddies. */
564 for (node
= purple_blist_node_get_first_child(node
); node
; node
= purple_blist_node_get_sibling_next(node
))
565 add_node(node
, FINCH_GET_DATA(list
));
567 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
568 if (!ggblist
->manager
->can_add_node(node
))
569 node_remove(list
, node
);
571 add_node(node
, FINCH_GET_DATA(list
));
573 if (ggblist
->tnode
== node
) {
574 draw_tooltip(ggblist
);
579 new_list(PurpleBuddyList
*list
)
584 ggblist
= g_new0(FinchBlist
, 1);
585 FINCH_SET_DATA(list
, ggblist
);
586 ggblist
->manager
= finch_blist_manager_find(purple_prefs_get_string(PREF_ROOT
"/grouping"));
587 if (!ggblist
->manager
)
588 ggblist
->manager
= &default_manager
;
592 remove_new_empty_group(gpointer data
)
594 PurpleBuddyList
*list
;
599 list
= purple_get_blist();
600 g_return_val_if_fail(list
, FALSE
);
602 ggblist
->new_group_timeout
= 0;
603 while (ggblist
->new_group
) {
604 PurpleBlistNode
*group
= ggblist
->new_group
->data
;
605 ggblist
->new_group
= g_list_delete_link(ggblist
->new_group
, ggblist
->new_group
);
606 node_update(list
, group
);
613 add_buddy_cb(void *data
, PurpleRequestFields
*allfields
)
615 const char *username
= purple_request_fields_get_string(allfields
, "screenname");
616 const char *alias
= purple_request_fields_get_string(allfields
, "alias");
617 const char *group
= purple_request_fields_get_string(allfields
, "group");
618 PurpleAccount
*account
= purple_request_fields_get_account(allfields
, "account");
619 const char *error
= NULL
;
624 error
= _("You must provide a username for the buddy.");
626 error
= _("You must provide a group.");
628 error
= _("You must select an account.");
629 else if (!purple_account_is_connected(account
))
630 error
= _("The selected account is not online.");
634 finch_request_add_buddy(account
, username
, group
, alias
);
635 purple_notify_error(NULL
, _("Error"), _("Error adding buddy"), error
);
639 grp
= purple_find_group(group
);
642 grp
= purple_group_new(group
);
643 purple_blist_add_group(grp
, NULL
);
646 /* XXX: Ask to merge if there's already a buddy with the same alias in the same group (#4553) */
648 if ((buddy
= purple_find_buddy_in_group(account
, username
, grp
)) == NULL
)
650 buddy
= purple_buddy_new(account
, username
, alias
);
651 purple_blist_add_buddy(buddy
, NULL
, grp
, NULL
);
654 purple_account_add_buddy(account
, buddy
);
658 finch_request_add_buddy(PurpleAccount
*account
, const char *username
, const char *grp
, const char *alias
)
660 PurpleRequestFields
*fields
= purple_request_fields_new();
661 PurpleRequestFieldGroup
*group
= purple_request_field_group_new(NULL
);
662 PurpleRequestField
*field
;
664 purple_request_fields_add_group(fields
, group
);
666 field
= purple_request_field_string_new("screenname", _("Username"), username
, FALSE
);
667 purple_request_field_group_add_field(group
, field
);
669 field
= purple_request_field_string_new("alias", _("Alias (optional)"), alias
, FALSE
);
670 purple_request_field_group_add_field(group
, field
);
672 field
= purple_request_field_string_new("group", _("Add in group"), grp
, FALSE
);
673 purple_request_field_group_add_field(group
, field
);
674 purple_request_field_set_type_hint(field
, "group");
676 field
= purple_request_field_account_new("account", _("Account"), NULL
);
677 purple_request_field_account_set_show_all(field
, FALSE
);
679 purple_request_field_account_set_value(field
, account
);
680 purple_request_field_group_add_field(group
, field
);
682 purple_request_fields(NULL
, _("Add Buddy"), NULL
, _("Please enter buddy information."),
684 _("Add"), G_CALLBACK(add_buddy_cb
),
691 join_chat(PurpleChat
*chat
)
693 PurpleAccount
*account
= purple_chat_get_account(chat
);
695 PurpleConversation
*conv
;
698 /* This hack here is to work around the fact that there's no good way of
699 * getting the actual name of a chat. I don't understand why we return
700 * the alias for a chat when all we want is the name. */
703 name
= purple_chat_get_name(chat
);
704 conv
= purple_find_conversation_with_account(
705 PURPLE_CONV_TYPE_CHAT
, name
, account
);
706 chat
->alias
= (char *)alias
;
708 if (!conv
|| purple_conv_chat_has_left(PURPLE_CONV_CHAT(conv
))) {
709 serv_join_chat(purple_account_get_connection(account
),
710 purple_chat_get_components(chat
));
712 purple_conversation_present(conv
);
717 add_chat_cb(void *data
, PurpleRequestFields
*allfields
)
719 PurpleAccount
*account
;
720 const char *alias
, *name
, *group
;
723 GHashTable
*hash
= NULL
;
724 PurpleConnection
*gc
;
726 PurplePluginProtocolInfo
*info
;
728 account
= purple_request_fields_get_account(allfields
, "account");
729 name
= purple_request_fields_get_string(allfields
, "name");
730 alias
= purple_request_fields_get_string(allfields
, "alias");
731 group
= purple_request_fields_get_string(allfields
, "group");
732 autojoin
= purple_request_fields_get_bool(allfields
, "autojoin");
734 if (!purple_account_is_connected(account
) || !name
|| !*name
)
737 if (!group
|| !*group
)
740 gc
= purple_account_get_connection(account
);
741 info
= PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc
));
742 if (info
->chat_info_defaults
!= NULL
)
743 hash
= info
->chat_info_defaults(gc
, name
);
745 chat
= purple_chat_new(account
, name
, hash
);
748 if ((grp
= purple_find_group(group
)) == NULL
) {
749 grp
= purple_group_new(group
);
750 purple_blist_add_group(grp
, NULL
);
752 purple_blist_add_chat(chat
, grp
, NULL
);
753 purple_blist_alias_chat(chat
, alias
);
754 purple_blist_node_set_bool((PurpleBlistNode
*)chat
, "gnt-autojoin", autojoin
);
762 finch_request_add_chat(PurpleAccount
*account
, PurpleGroup
*grp
, const char *alias
, const char *name
)
764 PurpleRequestFields
*fields
= purple_request_fields_new();
765 PurpleRequestFieldGroup
*group
= purple_request_field_group_new(NULL
);
766 PurpleRequestField
*field
;
768 purple_request_fields_add_group(fields
, group
);
770 field
= purple_request_field_account_new("account", _("Account"), NULL
);
771 purple_request_field_account_set_show_all(field
, FALSE
);
773 purple_request_field_account_set_value(field
, account
);
774 purple_request_field_group_add_field(group
, field
);
776 field
= purple_request_field_string_new("name", _("Name"), name
, FALSE
);
777 purple_request_field_group_add_field(group
, field
);
779 field
= purple_request_field_string_new("alias", _("Alias"), alias
, FALSE
);
780 purple_request_field_group_add_field(group
, field
);
782 field
= purple_request_field_string_new("group", _("Group"), grp
? purple_group_get_name(grp
) : NULL
, FALSE
);
783 purple_request_field_group_add_field(group
, field
);
784 purple_request_field_set_type_hint(field
, "group");
786 field
= purple_request_field_bool_new("autojoin", _("Auto-join"), FALSE
);
787 purple_request_field_group_add_field(group
, field
);
789 purple_request_fields(NULL
, _("Add Chat"), NULL
,
790 _("You can edit more information from the context menu later."),
791 fields
, _("Add"), G_CALLBACK(add_chat_cb
), _("Cancel"), NULL
,
797 add_group_cb(gpointer null
, const char *group
)
801 if (!group
|| !*group
) {
802 purple_notify_error(NULL
, _("Error"), _("Error adding group"),
803 _("You must give a name for the group to add."));
807 grp
= purple_find_group(group
);
809 grp
= purple_group_new(group
);
810 purple_blist_add_group(grp
, NULL
);
816 /* Treat the group as a new group even if it had existed before. This should
817 * make things easier to add buddies to empty groups (new or old) without having
818 * to turn on 'show empty groups' setting */
819 ggblist
->new_group
= g_list_prepend(ggblist
->new_group
, grp
);
820 if (ggblist
->new_group_timeout
)
821 purple_timeout_remove(ggblist
->new_group_timeout
);
822 ggblist
->new_group_timeout
= purple_timeout_add_seconds(SHOW_EMPTY_GROUP_TIMEOUT
,
823 remove_new_empty_group
, NULL
);
825 /* Select the group */
827 FinchBlistNode
*fnode
= FINCH_GET_DATA((PurpleBlistNode
*)grp
);
829 add_node((PurpleBlistNode
*)grp
, ggblist
);
830 gnt_tree_set_selected(GNT_TREE(ggblist
->tree
), grp
);
835 finch_request_add_group(void)
837 purple_request_input(NULL
, _("Add Group"), NULL
, _("Enter the name of the group"),
838 NULL
, FALSE
, FALSE
, NULL
,
839 _("Add"), G_CALLBACK(add_group_cb
), _("Cancel"), NULL
,
844 static PurpleBlistUiOps blist_ui_ops
=
853 finch_request_add_buddy
,
854 finch_request_add_chat
,
855 finch_request_add_group
,
863 finch_blist_get_handle(void)
871 add_group(PurpleGroup
*group
, FinchBlist
*ggblist
)
874 PurpleBlistNode
*node
= (PurpleBlistNode
*)group
;
875 if (FINCH_GET_DATA(node
))
877 parent
= ggblist
->manager
->find_parent((PurpleBlistNode
*)group
);
878 create_finch_blist_node(node
, gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), group
,
879 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), get_display_name(node
)),
881 gnt_tree_set_expanded(GNT_TREE(ggblist
->tree
), node
,
882 !purple_blist_node_get_bool(node
, "collapsed"));
886 get_display_name(PurpleBlistNode
*node
)
888 static char text
[2096];
889 char status
[8] = " ";
890 const char *name
= NULL
;
892 if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
893 node
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
))); /* XXX: this can return NULL?! */
898 if (PURPLE_BLIST_NODE_IS_BUDDY(node
))
900 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
901 PurpleStatusPrimitive prim
;
902 PurplePresence
*presence
;
904 gboolean ascii
= gnt_ascii_only();
906 presence
= purple_buddy_get_presence(buddy
);
907 if (purple_presence_is_status_primitive_active(presence
, PURPLE_STATUS_MOBILE
))
908 strncpy(status
, ascii
? ":" : "☎", sizeof(status
) - 1);
910 now
= purple_presence_get_active_status(presence
);
912 prim
= purple_status_type_get_primitive(purple_status_get_type(now
));
915 case PURPLE_STATUS_OFFLINE
:
916 strncpy(status
, ascii
? "x" : "⊗", sizeof(status
) - 1);
918 case PURPLE_STATUS_AVAILABLE
:
919 strncpy(status
, ascii
? "o" : "â—¯", sizeof(status
) - 1);
922 strncpy(status
, ascii
? "." : "⊖", sizeof(status
) - 1);
926 name
= purple_buddy_get_alias(buddy
);
928 else if (PURPLE_BLIST_NODE_IS_CHAT(node
))
930 PurpleChat
*chat
= (PurpleChat
*)node
;
931 name
= purple_chat_get_name(chat
);
933 strncpy(status
, "~", sizeof(status
) - 1);
935 else if (PURPLE_BLIST_NODE_IS_GROUP(node
))
936 return purple_group_get_name((PurpleGroup
*)node
);
938 g_snprintf(text
, sizeof(text
) - 1, "%s %s", status
, name
);
944 add_chat(PurpleChat
*chat
, FinchBlist
*ggblist
)
947 PurpleBlistNode
*node
= (PurpleBlistNode
*)chat
;
948 if (FINCH_GET_DATA(node
))
950 if (!purple_account_is_connected(purple_chat_get_account(chat
)))
953 parent
= ggblist
->manager
->find_parent((PurpleBlistNode
*)chat
);
955 create_finch_blist_node(node
, gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), chat
,
956 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), get_display_name(node
)),
961 add_contact(PurpleContact
*contact
, FinchBlist
*ggblist
)
964 PurpleBlistNode
*node
= (PurpleBlistNode
*)contact
;
967 if (FINCH_GET_DATA(node
))
970 name
= get_display_name(node
);
974 parent
= ggblist
->manager
->find_parent((PurpleBlistNode
*)contact
);
976 create_finch_blist_node(node
, gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), contact
,
977 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), name
),
980 gnt_tree_set_expanded(GNT_TREE(ggblist
->tree
), contact
, FALSE
);
984 add_buddy(PurpleBuddy
*buddy
, FinchBlist
*ggblist
)
987 PurpleBlistNode
*node
= (PurpleBlistNode
*)buddy
;
988 PurpleContact
*contact
;
990 if (FINCH_GET_DATA(node
))
993 contact
= purple_buddy_get_contact(buddy
);
994 parent
= ggblist
->manager
->find_parent((PurpleBlistNode
*)buddy
);
996 create_finch_blist_node(node
, gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), buddy
,
997 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), get_display_name(node
)),
1000 blist_update_row_flags((PurpleBlistNode
*)buddy
);
1001 if (buddy
== purple_contact_get_priority_buddy(contact
))
1002 blist_update_row_flags((PurpleBlistNode
*)contact
);
1007 buddy_signed_on(PurpleBuddy
*buddy
, FinchBlist
*ggblist
)
1009 add_node((PurpleBlistNode
*)buddy
, ggblist
);
1013 buddy_signed_off(PurpleBuddy
*buddy
, FinchBlist
*ggblist
)
1015 node_remove(purple_get_blist(), (PurpleBlistNode
*)buddy
);
1019 PurpleBlistUiOps
*finch_blist_get_ui_ops()
1021 return &blist_ui_ops
;
1025 selection_activate(GntWidget
*widget
, FinchBlist
*ggblist
)
1027 GntTree
*tree
= GNT_TREE(ggblist
->tree
);
1028 PurpleBlistNode
*node
= gnt_tree_get_selection_data(tree
);
1033 if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
1034 node
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
)));
1036 if (PURPLE_BLIST_NODE_IS_BUDDY(node
))
1038 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
1039 PurpleConversation
*conv
;
1040 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
,
1041 purple_buddy_get_name(buddy
),
1042 purple_buddy_get_account(buddy
));
1044 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
,
1045 purple_buddy_get_account(buddy
),
1046 purple_buddy_get_name(buddy
));
1048 FinchConv
*ggconv
= FINCH_GET_DATA(conv
);
1049 gnt_window_present(ggconv
->window
);
1051 finch_conversation_set_active(conv
);
1053 else if (PURPLE_BLIST_NODE_IS_CHAT(node
))
1055 join_chat((PurpleChat
*)node
);
1060 context_menu_callback(GntMenuItem
*item
, gpointer data
)
1062 PurpleMenuAction
*action
= data
;
1063 PurpleBlistNode
*node
= ggblist
->cnode
;
1065 void (*callback
)(PurpleBlistNode
*, gpointer
);
1066 callback
= (void (*)(PurpleBlistNode
*, gpointer
))action
->callback
;
1068 callback(node
, action
->data
);
1075 gnt_append_menu_action(GntMenu
*menu
, PurpleMenuAction
*action
, gpointer parent
)
1083 item
= gnt_menuitem_new(action
->label
);
1084 if (action
->callback
)
1085 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), context_menu_callback
, action
);
1086 gnt_menu_add_item(menu
, GNT_MENU_ITEM(item
));
1088 if (action
->children
) {
1089 GntWidget
*sub
= gnt_menu_new(GNT_MENU_POPUP
);
1090 gnt_menuitem_set_submenu(item
, GNT_MENU(sub
));
1091 for (list
= action
->children
; list
; list
= list
->next
)
1092 gnt_append_menu_action(GNT_MENU(sub
), list
->data
, action
);
1097 append_proto_menu(GntMenu
*menu
, PurpleConnection
*gc
, PurpleBlistNode
*node
)
1100 PurplePluginProtocolInfo
*prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc
));
1102 if(!prpl_info
|| !prpl_info
->blist_node_menu
)
1105 for(list
= prpl_info
->blist_node_menu(node
); list
;
1106 list
= g_list_delete_link(list
, list
))
1108 PurpleMenuAction
*act
= (PurpleMenuAction
*) list
->data
;
1110 gnt_append_menu_action(menu
, act
, NULL
);
1115 add_custom_action(GntMenu
*menu
, const char *label
, PurpleCallback callback
,
1118 PurpleMenuAction
*action
= purple_menu_action_new(label
, callback
, data
, NULL
);
1119 gnt_append_menu_action(menu
, action
, NULL
);
1120 g_signal_connect_swapped(G_OBJECT(menu
), "destroy",
1121 G_CALLBACK(purple_menu_action_free
), action
);
1125 chat_components_edit_ok(PurpleChat
*chat
, PurpleRequestFields
*allfields
)
1127 GList
*groups
, *fields
;
1129 for (groups
= purple_request_fields_get_groups(allfields
); groups
; groups
= groups
->next
) {
1130 fields
= purple_request_field_group_get_fields(groups
->data
);
1131 for (; fields
; fields
= fields
->next
) {
1132 PurpleRequestField
*field
= fields
->data
;
1136 id
= purple_request_field_get_id(field
);
1137 if (purple_request_field_get_type(field
) == PURPLE_REQUEST_FIELD_INTEGER
)
1138 val
= g_strdup_printf("%d", purple_request_field_int_get_value(field
));
1140 val
= g_strdup(purple_request_field_string_get_value(field
));
1143 g_hash_table_remove(purple_chat_get_components(chat
), id
);
1145 g_hash_table_replace(purple_chat_get_components(chat
), g_strdup(id
), val
); /* val should not be free'd */
1152 chat_components_edit(PurpleBlistNode
*selected
, PurpleChat
*chat
)
1154 PurpleRequestFields
*fields
= purple_request_fields_new();
1155 PurpleRequestFieldGroup
*group
= purple_request_field_group_new(NULL
);
1156 PurpleRequestField
*field
;
1157 GList
*parts
, *iter
;
1158 struct proto_chat_entry
*pce
;
1159 PurpleConnection
*gc
;
1161 purple_request_fields_add_group(fields
, group
);
1163 gc
= purple_account_get_connection(purple_chat_get_account(chat
));
1164 parts
= PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc
))->chat_info(gc
);
1166 for (iter
= parts
; iter
; iter
= iter
->next
) {
1170 const char *str
= g_hash_table_lookup(purple_chat_get_components(chat
), pce
->identifier
);
1171 if (!str
|| sscanf(str
, "%d", &val
) != 1)
1173 field
= purple_request_field_int_new(pce
->identifier
, pce
->label
, val
);
1175 field
= purple_request_field_string_new(pce
->identifier
, pce
->label
,
1176 g_hash_table_lookup(purple_chat_get_components(chat
), pce
->identifier
), FALSE
);
1178 purple_request_field_string_set_masked(field
, TRUE
);
1182 purple_request_field_set_required(field
, TRUE
);
1184 purple_request_field_group_add_field(group
, field
);
1190 purple_request_fields(NULL
, _("Edit Chat"), NULL
, _("Please Update the necessary fields."),
1191 fields
, _("Edit"), G_CALLBACK(chat_components_edit_ok
), _("Cancel"), NULL
,
1197 autojoin_toggled(GntMenuItem
*item
, gpointer data
)
1199 PurpleMenuAction
*action
= data
;
1200 purple_blist_node_set_bool(action
->data
, "gnt-autojoin",
1201 gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item
)));
1205 create_chat_menu(GntMenu
*menu
, PurpleChat
*chat
)
1207 PurpleMenuAction
*action
= purple_menu_action_new(_("Auto-join"), NULL
, chat
, NULL
);
1208 GntMenuItem
*check
= gnt_menuitem_check_new(action
->label
);
1209 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(check
),
1210 purple_blist_node_get_bool((PurpleBlistNode
*)chat
, "gnt-autojoin"));
1211 gnt_menu_add_item(menu
, check
);
1212 gnt_menuitem_set_callback(check
, autojoin_toggled
, action
);
1213 g_signal_connect_swapped(G_OBJECT(menu
), "destroy",
1214 G_CALLBACK(purple_menu_action_free
), action
);
1216 add_custom_action(menu
, _("Edit Settings"), (PurpleCallback
)chat_components_edit
, chat
);
1220 finch_add_buddy(PurpleBlistNode
*selected
, PurpleGroup
*grp
)
1222 purple_blist_request_add_buddy(NULL
, NULL
, grp
? purple_group_get_name(grp
) : NULL
, NULL
);
1226 finch_add_group(PurpleBlistNode
*selected
, PurpleGroup
*grp
)
1228 purple_blist_request_add_group();
1232 finch_add_chat(PurpleBlistNode
*selected
, PurpleGroup
*grp
)
1234 purple_blist_request_add_chat(NULL
, grp
, NULL
, NULL
);
1238 create_group_menu(GntMenu
*menu
, PurpleGroup
*group
)
1240 add_custom_action(menu
, _("Add Buddy"),
1241 PURPLE_CALLBACK(finch_add_buddy
), group
);
1242 add_custom_action(menu
, _("Add Chat"),
1243 PURPLE_CALLBACK(finch_add_chat
), group
);
1244 add_custom_action(menu
, _("Add Group"),
1245 PURPLE_CALLBACK(finch_add_group
), group
);
1248 gpointer
finch_retrieve_user_info(PurpleConnection
*conn
, const char *name
)
1250 PurpleNotifyUserInfo
*info
= purple_notify_user_info_new();
1252 purple_notify_user_info_add_pair(info
, _("Information"), _("Retrieving..."));
1253 uihandle
= purple_notify_userinfo(conn
, name
, info
, NULL
, NULL
);
1254 purple_notify_user_info_destroy(info
);
1256 serv_get_info(conn
, name
);
1261 finch_blist_get_buddy_info_cb(PurpleBlistNode
*selected
, PurpleBuddy
*buddy
)
1263 finch_retrieve_user_info(purple_account_get_connection(purple_buddy_get_account(buddy
)), purple_buddy_get_name(buddy
));
1267 finch_blist_menu_send_file_cb(PurpleBlistNode
*selected
, PurpleBuddy
*buddy
)
1269 serv_send_file(purple_account_get_connection(purple_buddy_get_account(buddy
)), purple_buddy_get_name(buddy
), NULL
);
1273 finch_blist_pounce_node_cb(PurpleBlistNode
*selected
, PurpleBlistNode
*node
)
1276 if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
1277 b
= purple_contact_get_priority_buddy((PurpleContact
*)node
);
1279 b
= (PurpleBuddy
*)node
;
1280 finch_pounce_editor_show(purple_buddy_get_account(b
), purple_buddy_get_name(b
), NULL
);
1284 toggle_block_buddy(GntMenuItem
*item
, gpointer buddy
)
1286 gboolean block
= gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item
));
1287 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1288 const char *name
= purple_buddy_get_name(buddy
);
1290 block
? purple_privacy_deny(account
, name
, FALSE
, FALSE
) :
1291 purple_privacy_allow(account
, name
, FALSE
, FALSE
);
1295 toggle_show_offline(GntMenuItem
*item
, gpointer buddy
)
1297 purple_blist_node_set_bool(buddy
, "show_offline",
1298 !purple_blist_node_get_bool(buddy
, "show_offline"));
1299 if (!ggblist
->manager
->can_add_node(buddy
))
1300 node_remove(purple_get_blist(), buddy
);
1302 node_update(purple_get_blist(), buddy
);
1306 create_buddy_menu(GntMenu
*menu
, PurpleBuddy
*buddy
)
1308 PurpleAccount
*account
;
1311 PurplePluginProtocolInfo
*prpl_info
;
1312 PurpleConnection
*gc
= purple_account_get_connection(purple_buddy_get_account(buddy
));
1314 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc
));
1315 if (prpl_info
&& prpl_info
->get_info
)
1317 add_custom_action(menu
, _("Get Info"),
1318 PURPLE_CALLBACK(finch_blist_get_buddy_info_cb
), buddy
);
1321 add_custom_action(menu
, _("Add Buddy Pounce"),
1322 PURPLE_CALLBACK(finch_blist_pounce_node_cb
), buddy
);
1324 if (prpl_info
&& prpl_info
->send_file
)
1326 if (!prpl_info
->can_receive_file
||
1327 prpl_info
->can_receive_file(gc
, purple_buddy_get_name(buddy
)))
1328 add_custom_action(menu
, _("Send File"),
1329 PURPLE_CALLBACK(finch_blist_menu_send_file_cb
), buddy
);
1332 account
= purple_buddy_get_account(buddy
);
1333 permitted
= purple_privacy_check(account
, purple_buddy_get_name(buddy
));
1335 item
= gnt_menuitem_check_new(_("Blocked"));
1336 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item
), !permitted
);
1337 gnt_menuitem_set_callback(item
, toggle_block_buddy
, buddy
);
1338 gnt_menu_add_item(menu
, item
);
1340 item
= gnt_menuitem_check_new(_("Show when offline"));
1341 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item
), purple_blist_node_get_bool((PurpleBlistNode
*)buddy
, "show_offline"));
1342 gnt_menuitem_set_callback(item
, toggle_show_offline
, buddy
);
1343 gnt_menu_add_item(menu
, item
);
1345 /* Protocol actions */
1346 append_proto_menu(menu
,
1347 purple_account_get_connection(purple_buddy_get_account(buddy
)),
1348 (PurpleBlistNode
*)buddy
);
1352 append_extended_menu(GntMenu
*menu
, PurpleBlistNode
*node
)
1356 for (iter
= purple_blist_node_get_extended_menu(node
);
1357 iter
; iter
= g_list_delete_link(iter
, iter
))
1359 gnt_append_menu_action(menu
, iter
->data
, NULL
);
1363 /* Xerox'd from gtkdialogs.c:purple_gtkdialogs_remove_contact_cb */
1365 remove_contact(PurpleContact
*contact
)
1367 PurpleBlistNode
*bnode
, *cnode
;
1370 cnode
= (PurpleBlistNode
*)contact
;
1371 group
= (PurpleGroup
*)purple_blist_node_get_parent(cnode
);
1372 for (bnode
= purple_blist_node_get_first_child(cnode
); bnode
; bnode
= purple_blist_node_get_sibling_next(bnode
)) {
1373 PurpleBuddy
*buddy
= (PurpleBuddy
*)bnode
;
1374 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1375 if (purple_account_is_connected(account
))
1376 purple_account_remove_buddy(account
, buddy
, group
);
1378 purple_blist_remove_contact(contact
);
1382 rename_blist_node(PurpleBlistNode
*node
, const char *newname
)
1384 const char *name
= newname
;
1388 if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1389 PurpleContact
*contact
= (PurpleContact
*)node
;
1390 PurpleBuddy
*buddy
= purple_contact_get_priority_buddy(contact
);
1391 purple_blist_alias_contact(contact
, name
);
1392 purple_blist_alias_buddy(buddy
, name
);
1393 serv_alias_buddy(buddy
);
1394 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1395 purple_blist_alias_buddy((PurpleBuddy
*)node
, name
);
1396 serv_alias_buddy((PurpleBuddy
*)node
);
1397 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
))
1398 purple_blist_alias_chat((PurpleChat
*)node
, name
);
1399 else if (PURPLE_BLIST_NODE_IS_GROUP(node
) && (name
!= NULL
))
1400 purple_blist_rename_group((PurpleGroup
*)node
, name
);
1402 g_return_if_reached();
1406 finch_blist_rename_node_cb(PurpleBlistNode
*selected
, PurpleBlistNode
*node
)
1408 const char *name
= NULL
;
1412 if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
1413 name
= purple_contact_get_alias((PurpleContact
*)node
);
1414 else if (PURPLE_BLIST_NODE_IS_BUDDY(node
))
1415 name
= purple_buddy_get_contact_alias((PurpleBuddy
*)node
);
1416 else if (PURPLE_BLIST_NODE_IS_CHAT(node
))
1417 name
= purple_chat_get_name((PurpleChat
*)node
);
1418 else if (PURPLE_BLIST_NODE_IS_GROUP(node
))
1419 name
= purple_group_get_name((PurpleGroup
*)node
);
1421 g_return_if_reached();
1423 prompt
= g_strdup_printf(_("Please enter the new name for %s"), name
);
1425 text
= PURPLE_BLIST_NODE_IS_GROUP(node
) ? _("Rename") : _("Set Alias");
1426 purple_request_input(node
, text
, prompt
, _("Enter empty string to reset the name."),
1427 name
, FALSE
, FALSE
, NULL
, text
, G_CALLBACK(rename_blist_node
),
1436 static void showlog_cb(PurpleBlistNode
*sel
, PurpleBlistNode
*node
)
1439 PurpleAccount
*account
;
1442 if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1443 PurpleBuddy
*b
= (PurpleBuddy
*) node
;
1444 type
= PURPLE_LOG_IM
;
1445 name
= g_strdup(purple_buddy_get_name(b
));
1446 account
= purple_buddy_get_account(b
);
1447 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
1448 PurpleChat
*c
= (PurpleChat
*) node
;
1449 PurplePluginProtocolInfo
*prpl_info
= NULL
;
1450 type
= PURPLE_LOG_CHAT
;
1451 account
= purple_chat_get_account(c
);
1452 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(purple_find_prpl(purple_account_get_protocol_id(account
)));
1453 if (prpl_info
&& prpl_info
->get_chat_name
) {
1454 name
= prpl_info
->get_chat_name(purple_chat_get_components(c
));
1456 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1457 finch_log_show_contact((PurpleContact
*)node
);
1460 /* This callback should not have been registered for a node
1461 * that doesn't match the type of one of the blocks above. */
1462 g_return_if_reached();
1465 if (name
&& account
) {
1466 finch_log_show(type
, name
, account
);
1472 /* Xeroxed from gtkdialogs.c:purple_gtkdialogs_remove_group_cb*/
1474 remove_group(PurpleGroup
*group
)
1476 PurpleBlistNode
*cnode
, *bnode
;
1478 cnode
= purple_blist_node_get_first_child(((PurpleBlistNode
*)group
));
1481 if (PURPLE_BLIST_NODE_IS_CONTACT(cnode
)) {
1482 bnode
= purple_blist_node_get_first_child(cnode
);
1483 cnode
= purple_blist_node_get_sibling_next(cnode
);
1486 if (PURPLE_BLIST_NODE_IS_BUDDY(bnode
)) {
1487 PurpleAccount
*account
;
1488 buddy
= (PurpleBuddy
*)bnode
;
1489 bnode
= purple_blist_node_get_sibling_next(bnode
);
1490 account
= purple_buddy_get_account(buddy
);
1491 if (purple_account_is_connected(account
)) {
1492 purple_account_remove_buddy(account
, buddy
, group
);
1493 purple_blist_remove_buddy(buddy
);
1496 bnode
= purple_blist_node_get_sibling_next(bnode
);
1499 } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode
)) {
1500 PurpleChat
*chat
= (PurpleChat
*)cnode
;
1501 cnode
= purple_blist_node_get_sibling_next(cnode
);
1502 if (purple_account_is_connected(purple_chat_get_account(chat
)))
1503 purple_blist_remove_chat(chat
);
1505 cnode
= purple_blist_node_get_sibling_next(cnode
);
1509 purple_blist_remove_group(group
);
1513 finch_blist_remove_node(PurpleBlistNode
*node
)
1515 if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1516 remove_contact((PurpleContact
*)node
);
1517 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1518 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
1519 PurpleGroup
*group
= purple_buddy_get_group(buddy
);
1520 purple_account_remove_buddy(purple_buddy_get_account(buddy
), buddy
, group
);
1521 purple_blist_remove_buddy(buddy
);
1522 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
1523 purple_blist_remove_chat((PurpleChat
*)node
);
1524 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
1525 remove_group((PurpleGroup
*)node
);
1530 finch_blist_remove_node_cb(PurpleBlistNode
*selected
, PurpleBlistNode
*node
)
1532 PurpleAccount
*account
= NULL
;
1534 const char *name
, *sec
= NULL
;
1536 if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1537 PurpleContact
*c
= (PurpleContact
*)node
;
1538 name
= purple_contact_get_alias(c
);
1539 if (c
->totalsize
> 1)
1540 sec
= _("Removing this contact will also remove all the buddies in the contact");
1541 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1542 name
= purple_buddy_get_name((PurpleBuddy
*)node
);
1543 account
= purple_buddy_get_account((PurpleBuddy
*)node
);
1544 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
1545 name
= purple_chat_get_name((PurpleChat
*)node
);
1546 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
1547 name
= purple_group_get_name((PurpleGroup
*)node
);
1548 sec
= _("Removing this group will also remove all the buddies in the group");
1553 primary
= g_strdup_printf(_("Are you sure you want to remove %s?"), name
);
1555 /* XXX: anything to do with the returned ui-handle? */
1556 purple_request_action(node
, _("Confirm Remove"),
1559 account
, name
, NULL
,
1561 _("Remove"), finch_blist_remove_node
,
1567 finch_blist_toggle_tag_buddy(PurpleBlistNode
*node
)
1572 if (ggblist
->tagged
&& (iter
= g_list_find(ggblist
->tagged
, node
)) != NULL
) {
1573 ggblist
->tagged
= g_list_delete_link(ggblist
->tagged
, iter
);
1575 ggblist
->tagged
= g_list_prepend(ggblist
->tagged
, node
);
1577 if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
1578 update_buddy_display(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
)), ggblist
);
1579 else if (PURPLE_BLIST_NODE_IS_BUDDY(node
))
1580 update_buddy_display((PurpleBuddy
*)node
, ggblist
);
1582 update_node_display(node
, ggblist
);
1586 finch_blist_place_tagged(PurpleBlistNode
*target
)
1588 PurpleGroup
*tg
= NULL
;
1589 PurpleContact
*tc
= NULL
;
1591 if (target
== NULL
||
1592 purple_blist_node_get_type(target
) == PURPLE_BLIST_OTHER_NODE
)
1595 if (PURPLE_BLIST_NODE_IS_GROUP(target
))
1596 tg
= (PurpleGroup
*)target
;
1597 else if (PURPLE_BLIST_NODE_IS_BUDDY(target
)) {
1598 tc
= (PurpleContact
*)purple_blist_node_get_parent(target
);
1599 tg
= (PurpleGroup
*)purple_blist_node_get_parent((PurpleBlistNode
*)tc
);
1601 if (PURPLE_BLIST_NODE_IS_CONTACT(target
))
1602 tc
= (PurpleContact
*)target
;
1603 tg
= (PurpleGroup
*)purple_blist_node_get_parent(target
);
1606 if (ggblist
->tagged
) {
1607 GList
*list
= ggblist
->tagged
;
1608 ggblist
->tagged
= NULL
;
1610 PurpleBlistNode
*node
= list
->data
;
1611 list
= g_list_delete_link(list
, list
);
1613 if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
1614 update_node_display(node
, ggblist
);
1615 /* Add the group after the current group */
1616 purple_blist_add_group((PurpleGroup
*)node
, (PurpleBlistNode
*)tg
);
1617 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1618 update_buddy_display(purple_contact_get_priority_buddy((PurpleContact
*)node
), ggblist
);
1619 if (PURPLE_BLIST_NODE(tg
) == target
) {
1620 /* The target is a group, just add the contact to the group. */
1621 purple_blist_add_contact((PurpleContact
*)node
, tg
, NULL
);
1623 /* The target is either a buddy, or a contact. Merge with that contact. */
1624 purple_blist_merge_contact((PurpleContact
*)node
, (PurpleBlistNode
*)tc
);
1626 /* The target is a chat. Add the contact to the group after this chat. */
1627 purple_blist_add_contact((PurpleContact
*)node
, NULL
, target
);
1629 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1630 update_buddy_display((PurpleBuddy
*)node
, ggblist
);
1631 if (PURPLE_BLIST_NODE(tg
) == target
) {
1632 /* The target is a group. Add this buddy in a new contact under this group. */
1633 purple_blist_add_buddy((PurpleBuddy
*)node
, NULL
, tg
, NULL
);
1634 } else if (PURPLE_BLIST_NODE_IS_CONTACT(target
)) {
1635 /* Add to the contact. */
1636 purple_blist_add_buddy((PurpleBuddy
*)node
, tc
, NULL
, NULL
);
1637 } else if (PURPLE_BLIST_NODE_IS_BUDDY(target
)) {
1638 /* Add to the contact after the selected buddy. */
1639 purple_blist_add_buddy((PurpleBuddy
*)node
, NULL
, NULL
, target
);
1640 } else if (PURPLE_BLIST_NODE_IS_CHAT(target
)) {
1641 /* Add to the selected chat's group. */
1642 purple_blist_add_buddy((PurpleBuddy
*)node
, NULL
, tg
, NULL
);
1644 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
1645 update_node_display(node
, ggblist
);
1646 if (PURPLE_BLIST_NODE(tg
) == target
)
1647 purple_blist_add_chat((PurpleChat
*)node
, tg
, NULL
);
1649 purple_blist_add_chat((PurpleChat
*)node
, NULL
, target
);
1656 context_menu_destroyed(GntWidget
*widget
, FinchBlist
*ggblist
)
1658 ggblist
->context
= NULL
;
1662 draw_context_menu(FinchBlist
*ggblist
)
1664 PurpleBlistNode
*node
= NULL
;
1665 GntWidget
*context
= NULL
;
1666 GntTree
*tree
= NULL
;
1667 int x
, y
, top
, width
;
1670 if (ggblist
->context
)
1673 tree
= GNT_TREE(ggblist
->tree
);
1675 node
= gnt_tree_get_selection_data(tree
);
1676 if (node
&& purple_blist_node_get_type(node
) == PURPLE_BLIST_OTHER_NODE
)
1679 if (ggblist
->tooltip
)
1680 remove_tooltip(ggblist
);
1682 ggblist
->cnode
= node
;
1684 ggblist
->context
= context
= gnt_menu_new(GNT_MENU_POPUP
);
1685 g_signal_connect(G_OBJECT(context
), "destroy", G_CALLBACK(context_menu_destroyed
), ggblist
);
1686 g_signal_connect(G_OBJECT(context
), "hide", G_CALLBACK(gnt_widget_destroy
), NULL
);
1689 create_group_menu(GNT_MENU(context
), NULL
);
1690 title
= g_strdup(_("Buddy List"));
1691 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1692 ggblist
->cnode
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node
)));
1693 create_buddy_menu(GNT_MENU(context
), (PurpleBuddy
*)ggblist
->cnode
);
1694 title
= g_strdup(purple_contact_get_alias((PurpleContact
*)node
));
1695 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1696 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
1697 create_buddy_menu(GNT_MENU(context
), buddy
);
1698 title
= g_strdup(purple_buddy_get_name(buddy
));
1699 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
1700 PurpleChat
*chat
= (PurpleChat
*)node
;
1701 create_chat_menu(GNT_MENU(context
), chat
);
1702 title
= g_strdup(purple_chat_get_name(chat
));
1703 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
1704 PurpleGroup
*group
= (PurpleGroup
*)node
;
1705 create_group_menu(GNT_MENU(context
), group
);
1706 title
= g_strdup(purple_group_get_name(group
));
1709 append_extended_menu(GNT_MENU(context
), node
);
1711 /* These are common for everything */
1713 add_custom_action(GNT_MENU(context
),
1714 PURPLE_BLIST_NODE_IS_GROUP(node
) ? _("Rename") : _("Alias"),
1715 PURPLE_CALLBACK(finch_blist_rename_node_cb
), node
);
1716 add_custom_action(GNT_MENU(context
), _("Remove"),
1717 PURPLE_CALLBACK(finch_blist_remove_node_cb
), node
);
1719 if (ggblist
->tagged
&& (PURPLE_BLIST_NODE_IS_CONTACT(node
)
1720 || PURPLE_BLIST_NODE_IS_GROUP(node
))) {
1721 add_custom_action(GNT_MENU(context
), _("Place tagged"),
1722 PURPLE_CALLBACK(finch_blist_place_tagged
), node
);
1725 if (PURPLE_BLIST_NODE_IS_BUDDY(node
) || PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1726 add_custom_action(GNT_MENU(context
), _("Toggle Tag"),
1727 PURPLE_CALLBACK(finch_blist_toggle_tag_buddy
), node
);
1729 if (!PURPLE_BLIST_NODE_IS_GROUP(node
)) {
1730 add_custom_action(GNT_MENU(context
), _("View Log"),
1731 PURPLE_CALLBACK(showlog_cb
), node
);
1735 /* Set the position for the popup */
1736 gnt_widget_get_position(GNT_WIDGET(tree
), &x
, &y
);
1737 gnt_widget_get_size(GNT_WIDGET(tree
), &width
, NULL
);
1738 top
= gnt_tree_get_selection_visible_line(tree
);
1743 gnt_widget_set_position(context
, x
, y
);
1744 gnt_screen_menu_show(GNT_MENU(context
));
1749 tooltip_for_buddy(PurpleBuddy
*buddy
, GString
*str
, gboolean full
)
1752 PurplePluginProtocolInfo
*prpl_info
;
1753 PurpleAccount
*account
;
1754 PurpleNotifyUserInfo
*user_info
;
1755 PurplePresence
*presence
;
1756 const char *alias
= purple_buddy_get_alias(buddy
);
1759 user_info
= purple_notify_user_info_new();
1761 account
= purple_buddy_get_account(buddy
);
1762 presence
= purple_buddy_get_presence(buddy
);
1764 if (!full
|| g_utf8_collate(purple_buddy_get_name(buddy
), alias
)) {
1765 char *esc
= g_markup_escape_text(alias
, -1);
1766 purple_notify_user_info_add_pair(user_info
, _("Nickname"), esc
);
1770 tmp
= g_strdup_printf("%s (%s)",
1771 purple_account_get_username(account
),
1772 purple_account_get_protocol_name(account
));
1773 purple_notify_user_info_add_pair(user_info
, _("Account"), tmp
);
1776 prpl
= purple_find_prpl(purple_account_get_protocol_id(account
));
1777 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
1778 if (prpl_info
&& prpl_info
->tooltip_text
) {
1779 prpl_info
->tooltip_text(buddy
, user_info
, full
);
1782 if (purple_prefs_get_bool("/finch/blist/idletime")) {
1783 PurplePresence
*pre
= purple_buddy_get_presence(buddy
);
1784 if (purple_presence_is_idle(pre
)) {
1785 time_t idle
= purple_presence_get_idle_time(pre
);
1787 char *st
= purple_str_seconds_to_string(time(NULL
) - idle
);
1788 purple_notify_user_info_add_pair(user_info
, _("Idle"), st
);
1794 tmp
= purple_notify_user_info_get_text_with_newline(user_info
, "<BR>");
1795 purple_notify_user_info_destroy(user_info
);
1797 strip
= purple_markup_strip_html(tmp
);
1798 g_string_append(str
, strip
);
1800 if (purple_presence_is_status_primitive_active(presence
, PURPLE_STATUS_MOBILE
)) {
1801 g_string_append(str
, "\n");
1802 g_string_append(str
, _("On Mobile"));
1810 make_sure_text_fits(GString
*string
)
1812 int maxw
= getmaxx(stdscr
) - 3;
1813 char *str
= gnt_util_onscreen_fit_string(string
->str
, maxw
);
1814 string
= g_string_assign(string
, str
);
1820 draw_tooltip_real(FinchBlist
*ggblist
)
1822 PurpleBlistNode
*node
;
1823 int x
, y
, top
, width
, w
, h
;
1824 GString
*str
= NULL
;
1826 GntWidget
*widget
, *box
, *tv
;
1829 widget
= ggblist
->tree
;
1830 tree
= GNT_TREE(widget
);
1832 if (!gnt_widget_has_focus(ggblist
->tree
) ||
1833 (ggblist
->context
&& !GNT_WIDGET_IS_FLAG_SET(ggblist
->context
, GNT_WIDGET_INVISIBLE
)))
1836 if (ggblist
->tooltip
)
1838 /* XXX: Once we can properly redraw on expose events, this can be removed at the end
1839 * to avoid the blinking*/
1840 remove_tooltip(ggblist
);
1843 node
= gnt_tree_get_selection_data(tree
);
1847 if (!ggblist
->manager
->create_tooltip(node
, &str
, &title
))
1850 gnt_widget_get_position(widget
, &x
, &y
);
1851 gnt_widget_get_size(widget
, &width
, NULL
);
1852 top
= gnt_tree_get_selection_visible_line(tree
);
1857 box
= gnt_box_new(FALSE
, FALSE
);
1858 gnt_box_set_toplevel(GNT_BOX(box
), TRUE
);
1859 GNT_WIDGET_SET_FLAGS(box
, GNT_WIDGET_NO_SHADOW
);
1860 gnt_box_set_title(GNT_BOX(box
), title
);
1862 str
= make_sure_text_fits(str
);
1863 gnt_util_get_text_bound(str
->str
, &w
, &h
);
1865 tv
= gnt_text_view_new();
1866 gnt_widget_set_size(tv
, w
+ 1, h
);
1867 gnt_text_view_set_flag(GNT_TEXT_VIEW(tv
), GNT_TEXT_VIEW_NO_SCROLL
);
1868 gnt_box_add_widget(GNT_BOX(box
), tv
);
1870 if (x
+ w
>= getmaxx(stdscr
))
1872 gnt_widget_set_position(box
, x
, y
);
1873 GNT_WIDGET_UNSET_FLAGS(box
, GNT_WIDGET_CAN_TAKE_FOCUS
);
1874 GNT_WIDGET_SET_FLAGS(box
, GNT_WIDGET_TRANSIENT
);
1875 gnt_widget_draw(box
);
1877 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(tv
), str
->str
, GNT_TEXT_FLAG_NORMAL
);
1878 gnt_text_view_scroll(GNT_TEXT_VIEW(tv
), 0);
1881 g_string_free(str
, TRUE
);
1882 ggblist
->tooltip
= box
;
1883 ggblist
->tnode
= node
;
1885 gnt_widget_set_name(ggblist
->tooltip
, "tooltip");
1890 draw_tooltip(FinchBlist
*ggblist
)
1892 /* When an account has signed off, it removes one buddy at a time.
1893 * Drawing the tooltip after removing each buddy is expensive. On
1894 * top of that, if the selected buddy belongs to the disconnected
1895 * account, then retreiving the tooltip for that causes crash. So
1896 * let's make sure we wait for all the buddies to be removed first.*/
1897 int id
= g_timeout_add(0, (GSourceFunc
)draw_tooltip_real
, ggblist
);
1898 g_object_set_data_full(G_OBJECT(ggblist
->window
), "draw_tooltip_calback",
1899 GINT_TO_POINTER(id
), (GDestroyNotify
)g_source_remove
);
1903 selection_changed(GntWidget
*widget
, gpointer old
, gpointer current
, FinchBlist
*ggblist
)
1905 remove_peripherals(ggblist
);
1906 draw_tooltip(ggblist
);
1910 context_menu(GntWidget
*widget
, FinchBlist
*ggblist
)
1912 draw_context_menu(ggblist
);
1917 key_pressed(GntWidget
*widget
, const char *text
, FinchBlist
*ggblist
)
1919 if (text
[0] == 27 && text
[1] == 0) {
1920 /* Escape was pressed */
1921 if (gnt_tree_is_searching(GNT_TREE(ggblist
->tree
)))
1922 gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist
->tree
), "end-search", NULL
);
1923 remove_peripherals(ggblist
);
1924 } else if (strcmp(text
, GNT_KEY_INS
) == 0) {
1925 PurpleBlistNode
*node
= gnt_tree_get_selection_data(GNT_TREE(ggblist
->tree
));
1926 purple_blist_request_add_buddy(NULL
, NULL
,
1927 node
&& PURPLE_BLIST_NODE_IS_GROUP(node
) ? purple_group_get_name(PURPLE_GROUP(node
)) : NULL
,
1929 } else if (!gnt_tree_is_searching(GNT_TREE(ggblist
->tree
))) {
1930 if (strcmp(text
, "t") == 0) {
1931 finch_blist_toggle_tag_buddy(gnt_tree_get_selection_data(GNT_TREE(ggblist
->tree
)));
1932 gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist
->tree
), "move-down");
1933 } else if (strcmp(text
, "a") == 0) {
1934 finch_blist_place_tagged(gnt_tree_get_selection_data(GNT_TREE(ggblist
->tree
)));
1944 update_node_display(PurpleBlistNode
*node
, FinchBlist
*ggblist
)
1946 GntTextFormatFlags flag
= get_blist_node_flag(node
);
1947 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), node
, flag
);
1951 update_buddy_display(PurpleBuddy
*buddy
, FinchBlist
*ggblist
)
1953 PurpleContact
*contact
;
1955 contact
= purple_buddy_get_contact(buddy
);
1957 gnt_tree_change_text(GNT_TREE(ggblist
->tree
), buddy
, 0, get_display_name((PurpleBlistNode
*)buddy
));
1958 gnt_tree_change_text(GNT_TREE(ggblist
->tree
), contact
, 0, get_display_name((PurpleBlistNode
*)contact
));
1960 blist_update_row_flags((PurpleBlistNode
*)buddy
);
1961 if (buddy
== purple_contact_get_priority_buddy(contact
))
1962 blist_update_row_flags((PurpleBlistNode
*)contact
);
1964 if (ggblist
->tnode
== (PurpleBlistNode
*)buddy
)
1965 draw_tooltip(ggblist
);
1969 buddy_status_changed(PurpleBuddy
*buddy
, PurpleStatus
*old
, PurpleStatus
*now
, FinchBlist
*ggblist
)
1971 update_buddy_display(buddy
, ggblist
);
1975 buddy_idle_changed(PurpleBuddy
*buddy
, int old
, int new, FinchBlist
*ggblist
)
1977 update_buddy_display(buddy
, ggblist
);
1981 remove_peripherals(FinchBlist
*ggblist
)
1983 if (ggblist
->tooltip
)
1984 remove_tooltip(ggblist
);
1985 else if (ggblist
->context
)
1986 gnt_widget_destroy(ggblist
->context
);
1990 size_changed_cb(GntWidget
*w
, int wi
, int h
)
1993 gnt_widget_get_size(w
, &width
, &height
);
1994 purple_prefs_set_int(PREF_ROOT
"/size/width", width
);
1995 purple_prefs_set_int(PREF_ROOT
"/size/height", height
);
1999 save_position_cb(GntWidget
*w
, int x
, int y
)
2001 purple_prefs_set_int(PREF_ROOT
"/position/x", x
);
2002 purple_prefs_set_int(PREF_ROOT
"/position/y", y
);
2006 reset_blist_window(GntWidget
*window
, gpointer null
)
2008 PurpleBlistNode
*node
;
2009 purple_signals_disconnect_by_handle(finch_blist_get_handle());
2010 FINCH_SET_DATA(purple_get_blist(), NULL
);
2012 node
= purple_blist_get_root();
2014 reset_blist_node_ui_data(node
);
2015 node
= purple_blist_node_next(node
, TRUE
);
2018 if (ggblist
->typing
)
2019 purple_timeout_remove(ggblist
->typing
);
2020 remove_peripherals(ggblist
);
2021 if (ggblist
->tagged
)
2022 g_list_free(ggblist
->tagged
);
2024 if (ggblist
->new_group_timeout
)
2025 purple_timeout_remove(ggblist
->new_group_timeout
);
2026 if (ggblist
->new_group
)
2027 g_list_free(ggblist
->new_group
);
2034 populate_buddylist(void)
2036 PurpleBlistNode
*node
;
2037 PurpleBuddyList
*list
;
2039 if (ggblist
->manager
->init
)
2040 ggblist
->manager
->init();
2042 if (strcmp(purple_prefs_get_string(PREF_ROOT
"/sort_type"), "text") == 0) {
2043 gnt_tree_set_compare_func(GNT_TREE(ggblist
->tree
),
2044 (GCompareFunc
)blist_node_compare_text
);
2045 } else if (strcmp(purple_prefs_get_string(PREF_ROOT
"/sort_type"), "status") == 0) {
2046 gnt_tree_set_compare_func(GNT_TREE(ggblist
->tree
),
2047 (GCompareFunc
)blist_node_compare_status
);
2048 } else if (strcmp(purple_prefs_get_string(PREF_ROOT
"/sort_type"), "log") == 0) {
2049 gnt_tree_set_compare_func(GNT_TREE(ggblist
->tree
),
2050 (GCompareFunc
)blist_node_compare_log
);
2053 list
= purple_get_blist();
2054 node
= purple_blist_get_root();
2057 node_update(list
, node
);
2058 node
= purple_blist_node_next(node
, FALSE
);
2063 destroy_status_list(GList
*list
)
2065 g_list_foreach(list
, (GFunc
)g_free
, NULL
);
2070 populate_status_dropdown(void)
2074 GList
*items
= NULL
;
2075 StatusBoxItem
*item
= NULL
;
2077 /* First the primitives */
2078 PurpleStatusPrimitive prims
[] = {PURPLE_STATUS_AVAILABLE
, PURPLE_STATUS_AWAY
,
2079 PURPLE_STATUS_INVISIBLE
, PURPLE_STATUS_OFFLINE
, PURPLE_STATUS_UNSET
};
2081 gnt_combo_box_remove_all(GNT_COMBO_BOX(ggblist
->status
));
2083 for (i
= 0; prims
[i
] != PURPLE_STATUS_UNSET
; i
++)
2085 item
= g_new0(StatusBoxItem
, 1);
2086 item
->type
= STATUS_PRIMITIVE
;
2087 item
->u
.prim
= prims
[i
];
2088 items
= g_list_prepend(items
, item
);
2089 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
2090 purple_primitive_get_name_from_type(prims
[i
]));
2093 /* Now the popular statuses */
2094 for (iter
= purple_savedstatuses_get_popular(6); iter
; iter
= g_list_delete_link(iter
, iter
))
2096 item
= g_new0(StatusBoxItem
, 1);
2097 item
->type
= STATUS_SAVED_POPULAR
;
2098 item
->u
.saved
= iter
->data
;
2099 items
= g_list_prepend(items
, item
);
2100 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
2101 purple_savedstatus_get_title(iter
->data
));
2104 /* New savedstatus */
2105 item
= g_new0(StatusBoxItem
, 1);
2106 item
->type
= STATUS_SAVED_NEW
;
2107 items
= g_list_prepend(items
, item
);
2108 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
2111 /* More savedstatuses */
2112 item
= g_new0(StatusBoxItem
, 1);
2113 item
->type
= STATUS_SAVED_ALL
;
2114 items
= g_list_prepend(items
, item
);
2115 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
2118 /* The keys for the combobox are created here, and never used
2119 * anywhere else. So make sure the keys are freed when the widget
2121 g_object_set_data_full(G_OBJECT(ggblist
->status
), "list of statuses",
2122 items
, (GDestroyNotify
)destroy_status_list
);
2126 redraw_blist(const char *name
, PurplePrefType type
, gconstpointer val
, gpointer data
)
2128 PurpleBlistNode
*node
, *sel
;
2129 FinchBlistManager
*manager
;
2131 if (ggblist
== NULL
)
2134 manager
= finch_blist_manager_find(purple_prefs_get_string(PREF_ROOT
"/grouping"));
2135 if (manager
== NULL
)
2136 manager
= &default_manager
;
2137 if (ggblist
->manager
!= manager
) {
2138 if (ggblist
->manager
->uninit
)
2139 ggblist
->manager
->uninit();
2141 ggblist
->manager
= manager
;
2142 if (manager
->can_add_node
== NULL
)
2143 manager
->can_add_node
= default_can_add_node
;
2144 if (manager
->find_parent
== NULL
)
2145 manager
->find_parent
= default_find_parent
;
2146 if (manager
->create_tooltip
== NULL
)
2147 manager
->create_tooltip
= default_create_tooltip
;
2150 if (ggblist
->window
== NULL
)
2153 sel
= gnt_tree_get_selection_data(GNT_TREE(ggblist
->tree
));
2154 gnt_tree_remove_all(GNT_TREE(ggblist
->tree
));
2156 node
= purple_blist_get_root();
2157 for (; node
; node
= purple_blist_node_next(node
, TRUE
))
2158 reset_blist_node_ui_data(node
);
2159 populate_buddylist();
2160 gnt_tree_set_selected(GNT_TREE(ggblist
->tree
), sel
);
2161 draw_tooltip(ggblist
);
2164 void finch_blist_init()
2166 color_available
= gnt_style_get_color(NULL
, "color-available");
2167 if (!color_available
)
2168 color_available
= gnt_color_add_pair(COLOR_GREEN
, -1);
2169 color_away
= gnt_style_get_color(NULL
, "color-away");
2171 color_away
= gnt_color_add_pair(COLOR_BLUE
, -1);
2172 color_idle
= gnt_style_get_color(NULL
, "color-idle");
2174 color_idle
= gnt_color_add_pair(COLOR_CYAN
, -1);
2175 color_offline
= gnt_style_get_color(NULL
, "color-offline");
2177 color_offline
= gnt_color_add_pair(COLOR_RED
, -1);
2179 purple_prefs_add_none(PREF_ROOT
);
2180 purple_prefs_add_none(PREF_ROOT
"/size");
2181 purple_prefs_add_int(PREF_ROOT
"/size/width", 20);
2182 purple_prefs_add_int(PREF_ROOT
"/size/height", 17);
2183 purple_prefs_add_none(PREF_ROOT
"/position");
2184 purple_prefs_add_int(PREF_ROOT
"/position/x", 0);
2185 purple_prefs_add_int(PREF_ROOT
"/position/y", 0);
2186 purple_prefs_add_bool(PREF_ROOT
"/idletime", TRUE
);
2187 purple_prefs_add_bool(PREF_ROOT
"/showoffline", FALSE
);
2188 purple_prefs_add_bool(PREF_ROOT
"/emptygroups", FALSE
);
2189 purple_prefs_add_string(PREF_ROOT
"/sort_type", "text");
2190 purple_prefs_add_string(PREF_ROOT
"/grouping", "default");
2192 purple_prefs_connect_callback(finch_blist_get_handle(),
2193 PREF_ROOT
"/emptygroups", redraw_blist
, NULL
);
2194 purple_prefs_connect_callback(finch_blist_get_handle(),
2195 PREF_ROOT
"/showoffline", redraw_blist
, NULL
);
2196 purple_prefs_connect_callback(finch_blist_get_handle(),
2197 PREF_ROOT
"/sort_type", redraw_blist
, NULL
);
2198 purple_prefs_connect_callback(finch_blist_get_handle(),
2199 PREF_ROOT
"/grouping", redraw_blist
, NULL
);
2201 purple_signal_connect(purple_connections_get_handle(), "signed-on", purple_blist_get_handle(),
2202 G_CALLBACK(account_signed_on_cb
), NULL
);
2204 finch_blist_install_manager(&default_manager
);
2210 remove_typing_cb(gpointer null
)
2212 PurpleSavedStatus
*current
;
2213 const char *message
, *newmessage
;
2214 char *escnewmessage
;
2215 PurpleStatusPrimitive prim
, newprim
;
2216 StatusBoxItem
*item
;
2218 current
= purple_savedstatus_get_current();
2219 message
= purple_savedstatus_get_message(current
);
2220 prim
= purple_savedstatus_get_type(current
);
2222 newmessage
= gnt_entry_get_text(GNT_ENTRY(ggblist
->statustext
));
2223 item
= gnt_combo_box_get_selected_data(GNT_COMBO_BOX(ggblist
->status
));
2224 escnewmessage
= newmessage
? g_markup_escape_text(newmessage
, -1) : NULL
;
2226 switch (item
->type
) {
2227 case STATUS_PRIMITIVE
:
2228 newprim
= item
->u
.prim
;
2230 case STATUS_SAVED_POPULAR
:
2231 newprim
= purple_savedstatus_get_type(item
->u
.saved
);
2234 goto end
; /* 'New' or 'Saved' is selected, but this should never happen. */
2237 if (newprim
!= prim
|| ((message
&& !escnewmessage
) ||
2238 (!message
&& escnewmessage
) ||
2239 (message
&& escnewmessage
&& g_utf8_collate(message
, escnewmessage
) != 0)))
2241 PurpleSavedStatus
*status
= purple_savedstatus_find_transient_by_type_and_message(newprim
, escnewmessage
);
2242 /* Holy Crap! That's a LAWNG function name */
2245 status
= purple_savedstatus_new(NULL
, newprim
);
2246 purple_savedstatus_set_message(status
, escnewmessage
);
2249 purple_savedstatus_activate(status
);
2252 gnt_box_give_focus_to_child(GNT_BOX(ggblist
->window
), ggblist
->tree
);
2254 g_free(escnewmessage
);
2255 if (ggblist
->typing
)
2256 purple_timeout_remove(ggblist
->typing
);
2257 ggblist
->typing
= 0;
2262 status_selection_changed(GntComboBox
*box
, StatusBoxItem
*old
, StatusBoxItem
*now
, gpointer null
)
2264 gnt_entry_set_text(GNT_ENTRY(ggblist
->statustext
), NULL
);
2265 if (now
->type
== STATUS_SAVED_POPULAR
)
2267 /* Set the status immediately */
2268 purple_savedstatus_activate(now
->u
.saved
);
2270 else if (now
->type
== STATUS_PRIMITIVE
)
2272 /* Move the focus to the entry box */
2273 /* XXX: Make sure the selected status can have a message */
2274 gnt_box_move_focus(GNT_BOX(ggblist
->window
), 1);
2275 ggblist
->typing
= purple_timeout_add_seconds(TYPING_TIMEOUT_S
, (GSourceFunc
)remove_typing_cb
, NULL
);
2277 else if (now
->type
== STATUS_SAVED_ALL
)
2279 /* Restore the selection to reflect current status. */
2280 savedstatus_changed(purple_savedstatus_get_current(), NULL
);
2281 gnt_box_give_focus_to_child(GNT_BOX(ggblist
->window
), ggblist
->tree
);
2282 finch_savedstatus_show_all();
2284 else if (now
->type
== STATUS_SAVED_NEW
)
2286 savedstatus_changed(purple_savedstatus_get_current(), NULL
);
2287 gnt_box_give_focus_to_child(GNT_BOX(ggblist
->window
), ggblist
->tree
);
2288 finch_savedstatus_edit(NULL
);
2291 g_return_if_reached();
2295 status_text_changed(GntEntry
*entry
, const char *text
, gpointer null
)
2297 if ((text
[0] == 27 || (text
[0] == '\t' && text
[1] == '\0')) && ggblist
->typing
== 0)
2300 if (ggblist
->typing
)
2301 purple_timeout_remove(ggblist
->typing
);
2302 ggblist
->typing
= 0;
2304 if (text
[0] == '\r' && text
[1] == 0)
2306 /* Set the status only after you press 'Enter' */
2307 remove_typing_cb(NULL
);
2311 ggblist
->typing
= purple_timeout_add_seconds(TYPING_TIMEOUT_S
, (GSourceFunc
)remove_typing_cb
, NULL
);
2316 savedstatus_changed(PurpleSavedStatus
*now
, PurpleSavedStatus
*old
)
2319 PurpleStatusPrimitive prim
;
2320 const char *message
;
2321 gboolean found
= FALSE
, saved
= TRUE
;
2326 /* Block the signals we don't want to emit */
2327 g_signal_handlers_block_matched(ggblist
->status
, G_SIGNAL_MATCH_FUNC
,
2328 0, 0, NULL
, status_selection_changed
, NULL
);
2329 g_signal_handlers_block_matched(ggblist
->statustext
, G_SIGNAL_MATCH_FUNC
,
2330 0, 0, NULL
, status_text_changed
, NULL
);
2332 prim
= purple_savedstatus_get_type(now
);
2333 message
= purple_savedstatus_get_message(now
);
2335 /* Rebuild the status dropdown */
2336 populate_status_dropdown();
2339 list
= g_object_get_data(G_OBJECT(ggblist
->status
), "list of statuses");
2340 for (; list
; list
= list
->next
)
2342 StatusBoxItem
*item
= list
->data
;
2343 if ((saved
&& item
->type
!= STATUS_PRIMITIVE
&& item
->u
.saved
== now
) ||
2344 (!saved
&& item
->type
== STATUS_PRIMITIVE
&& item
->u
.prim
== prim
))
2346 char *mess
= purple_unescape_html(message
);
2347 gnt_combo_box_set_selected(GNT_COMBO_BOX(ggblist
->status
), item
);
2348 gnt_entry_set_text(GNT_ENTRY(ggblist
->statustext
), mess
);
2349 gnt_widget_draw(ggblist
->status
);
2360 g_signal_handlers_unblock_matched(ggblist
->status
, G_SIGNAL_MATCH_FUNC
,
2361 0, 0, NULL
, status_selection_changed
, NULL
);
2362 g_signal_handlers_unblock_matched(ggblist
->statustext
, G_SIGNAL_MATCH_FUNC
,
2363 0, 0, NULL
, status_text_changed
, NULL
);
2367 blist_node_compare_position(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
2369 while ((n1
= purple_blist_node_get_sibling_prev(n1
)) != NULL
)
2376 blist_node_compare_text(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
2378 const char *s1
, *s2
;
2382 if (purple_blist_node_get_type(n1
) != purple_blist_node_get_type(n2
))
2383 return blist_node_compare_position(n1
, n2
);
2385 switch (purple_blist_node_get_type(n1
))
2387 case PURPLE_BLIST_CHAT_NODE
:
2388 s1
= purple_chat_get_name((PurpleChat
*)n1
);
2389 s2
= purple_chat_get_name((PurpleChat
*)n2
);
2391 case PURPLE_BLIST_BUDDY_NODE
:
2392 return purple_presence_compare(purple_buddy_get_presence((PurpleBuddy
*)n1
),
2393 purple_buddy_get_presence((PurpleBuddy
*)n2
));
2395 case PURPLE_BLIST_CONTACT_NODE
:
2396 s1
= purple_contact_get_alias((PurpleContact
*)n1
);
2397 s2
= purple_contact_get_alias((PurpleContact
*)n2
);
2400 return blist_node_compare_position(n1
, n2
);
2403 us1
= g_utf8_strup(s1
, -1);
2404 us2
= g_utf8_strup(s2
, -1);
2405 ret
= g_utf8_collate(us1
, us2
);
2413 blist_node_compare_status(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
2417 if (purple_blist_node_get_type(n1
) != purple_blist_node_get_type(n2
))
2418 return blist_node_compare_position(n1
, n2
);
2420 switch (purple_blist_node_get_type(n1
)) {
2421 case PURPLE_BLIST_CONTACT_NODE
:
2422 n1
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n1
)));
2423 n2
= PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n2
)));
2424 /* now compare the presence of the priority buddies */
2425 case PURPLE_BLIST_BUDDY_NODE
:
2426 ret
= purple_presence_compare(purple_buddy_get_presence((PurpleBuddy
*)n1
),
2427 purple_buddy_get_presence((PurpleBuddy
*)n2
));
2432 return blist_node_compare_position(n1
, n2
);
2436 /* Sort alphabetically if presence is not comparable */
2437 ret
= blist_node_compare_text(n1
, n2
);
2443 get_contact_log_size(PurpleBlistNode
*c
)
2446 PurpleBlistNode
*node
;
2448 for (node
= purple_blist_node_get_first_child(c
); node
; node
= purple_blist_node_get_sibling_next(node
)) {
2449 PurpleBuddy
*b
= (PurpleBuddy
*)node
;
2450 log
+= purple_log_get_total_size(PURPLE_LOG_IM
, purple_buddy_get_name(b
),
2451 purple_buddy_get_account(b
));
2458 blist_node_compare_log(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
2461 PurpleBuddy
*b1
, *b2
;
2463 if (purple_blist_node_get_type(n1
) != purple_blist_node_get_type(n2
))
2464 return blist_node_compare_position(n1
, n2
);
2466 switch (purple_blist_node_get_type(n1
)) {
2467 case PURPLE_BLIST_BUDDY_NODE
:
2468 b1
= (PurpleBuddy
*)n1
;
2469 b2
= (PurpleBuddy
*)n2
;
2470 ret
= purple_log_get_total_size(PURPLE_LOG_IM
, purple_buddy_get_name(b2
), purple_buddy_get_account(b2
)) -
2471 purple_log_get_total_size(PURPLE_LOG_IM
, purple_buddy_get_name(b1
), purple_buddy_get_account(b1
));
2475 case PURPLE_BLIST_CONTACT_NODE
:
2476 ret
= get_contact_log_size(n2
) - get_contact_log_size(n1
);
2481 return blist_node_compare_position(n1
, n2
);
2483 ret
= blist_node_compare_text(n1
, n2
);
2488 plugin_action(GntMenuItem
*item
, gpointer data
)
2490 PurplePluginAction
*action
= data
;
2491 if (action
&& action
->callback
)
2492 action
->callback(action
);
2496 build_plugin_actions(GntMenuItem
*item
, PurplePlugin
*plugin
, gpointer context
)
2498 GntWidget
*sub
= gnt_menu_new(GNT_MENU_POPUP
);
2500 GntMenuItem
*menuitem
;
2502 gnt_menuitem_set_submenu(item
, GNT_MENU(sub
));
2503 for (actions
= PURPLE_PLUGIN_ACTIONS(plugin
, context
); actions
;
2504 actions
= g_list_delete_link(actions
, actions
)) {
2505 if (actions
->data
) {
2506 PurplePluginAction
*action
= actions
->data
;
2507 action
->plugin
= plugin
;
2508 action
->context
= context
;
2509 menuitem
= gnt_menuitem_new(action
->label
);
2510 gnt_menu_add_item(GNT_MENU(sub
), menuitem
);
2512 gnt_menuitem_set_callback(menuitem
, plugin_action
, action
);
2513 g_object_set_data_full(G_OBJECT(menuitem
), "plugin_action",
2514 action
, (GDestroyNotify
)purple_plugin_action_free
);
2520 buddy_recent_signed_on_off(gpointer data
)
2522 PurpleBlistNode
*node
= data
;
2523 FinchBlistNode
*fnode
= FINCH_GET_DATA(node
);
2525 purple_timeout_remove(fnode
->signed_timer
);
2526 fnode
->signed_timer
= 0;
2528 if (!ggblist
->manager
->can_add_node(node
)) {
2529 node_remove(purple_get_blist(), node
);
2531 update_node_display(node
, ggblist
);
2532 if (purple_blist_node_get_parent(node
) && PURPLE_BLIST_NODE_IS_CONTACT(purple_blist_node_get_parent(node
)))
2533 update_node_display(purple_blist_node_get_parent(node
), ggblist
);
2540 buddy_signed_on_off_cb(gpointer data
)
2542 PurpleBlistNode
*node
= data
;
2543 FinchBlistNode
*fnode
= FINCH_GET_DATA(node
);
2544 if (!ggblist
|| !fnode
)
2547 if (fnode
->signed_timer
)
2548 purple_timeout_remove(fnode
->signed_timer
);
2549 fnode
->signed_timer
= purple_timeout_add_seconds(6, (GSourceFunc
)buddy_recent_signed_on_off
, data
);
2550 update_node_display(node
, ggblist
);
2551 if (purple_blist_node_get_parent(node
) && PURPLE_BLIST_NODE_IS_CONTACT(purple_blist_node_get_parent(node
)))
2552 update_node_display(purple_blist_node_get_parent(node
), ggblist
);
2557 buddy_signed_on_off(PurpleBuddy
* buddy
, gpointer null
)
2559 g_idle_add(buddy_signed_on_off_cb
, buddy
);
2563 reconstruct_plugins_menu(void)
2572 if (ggblist
->plugins
== NULL
)
2573 ggblist
->plugins
= gnt_menuitem_new(_("Plugins"));
2575 plg
= ggblist
->plugins
;
2576 sub
= gnt_menu_new(GNT_MENU_POPUP
);
2577 gnt_menuitem_set_submenu(plg
, GNT_MENU(sub
));
2579 for (iter
= purple_plugins_get_loaded(); iter
; iter
= iter
->next
) {
2580 PurplePlugin
*plugin
= iter
->data
;
2582 if (PURPLE_IS_PROTOCOL_PLUGIN(plugin
))
2585 if (!PURPLE_PLUGIN_HAS_ACTIONS(plugin
))
2588 item
= gnt_menuitem_new(_(plugin
->info
->name
));
2589 gnt_menu_add_item(GNT_MENU(sub
), item
);
2590 build_plugin_actions(item
, plugin
, NULL
);
2595 reconstruct_accounts_menu(void)
2598 GntMenuItem
*acc
, *item
;
2604 if (ggblist
->accounts
== NULL
)
2605 ggblist
->accounts
= gnt_menuitem_new(_("Accounts"));
2607 acc
= ggblist
->accounts
;
2608 sub
= gnt_menu_new(GNT_MENU_POPUP
);
2609 gnt_menuitem_set_submenu(acc
, GNT_MENU(sub
));
2611 for (iter
= purple_accounts_get_all_active(); iter
;
2612 iter
= g_list_delete_link(iter
, iter
)) {
2613 PurpleAccount
*account
= iter
->data
;
2614 PurpleConnection
*gc
= purple_account_get_connection(account
);
2617 if (!gc
|| !PURPLE_CONNECTION_IS_CONNECTED(gc
))
2619 prpl
= purple_connection_get_prpl(gc
);
2621 if (PURPLE_PLUGIN_HAS_ACTIONS(prpl
)) {
2622 item
= gnt_menuitem_new(purple_account_get_username(account
));
2623 gnt_menu_add_item(GNT_MENU(sub
), item
);
2624 build_plugin_actions(item
, prpl
, gc
);
2630 reconstruct_grouping_menu(void)
2635 if (!ggblist
|| !ggblist
->grouping
)
2638 subsub
= gnt_menu_new(GNT_MENU_POPUP
);
2639 gnt_menuitem_set_submenu(ggblist
->grouping
, GNT_MENU(subsub
));
2641 for (iter
= managers
; iter
; iter
= iter
->next
) {
2643 FinchBlistManager
*manager
= iter
->data
;
2644 GntMenuItem
*item
= gnt_menuitem_new(_(manager
->name
));
2645 g_snprintf(menuid
, sizeof(menuid
), "grouping-%s", manager
->id
);
2646 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), menuid
);
2647 gnt_menu_add_item(GNT_MENU(subsub
), item
);
2648 g_object_set_data_full(G_OBJECT(item
), "grouping-id", g_strdup(manager
->id
), g_free
);
2649 gnt_menuitem_set_callback(item
, menu_group_set_cb
, NULL
);
2654 auto_join_chats(gpointer data
)
2656 PurpleBlistNode
*node
;
2657 PurpleConnection
*pc
= data
;
2658 PurpleAccount
*account
= purple_connection_get_account(pc
);
2660 for (node
= purple_blist_get_root(); node
;
2661 node
= purple_blist_node_next(node
, FALSE
)) {
2662 if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
2663 PurpleChat
*chat
= (PurpleChat
*)node
;
2664 if (purple_chat_get_account(chat
) == account
&&
2665 purple_blist_node_get_bool(node
, "gnt-autojoin"))
2666 serv_join_chat(purple_account_get_connection(account
), purple_chat_get_components(chat
));
2673 account_signed_on_cb(PurpleConnection
*gc
, gpointer null
)
2675 g_idle_add(auto_join_chats
, gc
);
2678 static void toggle_pref_cb(GntMenuItem
*item
, gpointer n
)
2680 purple_prefs_set_bool(n
, !purple_prefs_get_bool(n
));
2683 static void sort_blist_change_cb(GntMenuItem
*item
, gpointer n
)
2685 purple_prefs_set_string(PREF_ROOT
"/sort_type", n
);
2689 block_select_cb(gpointer data
, PurpleRequestFields
*fields
)
2691 PurpleAccount
*account
= purple_request_fields_get_account(fields
, "account");
2692 const char *name
= purple_request_fields_get_string(fields
, "screenname");
2693 if (account
&& name
&& *name
!= '\0') {
2694 if (purple_request_fields_get_choice(fields
, "block") == 1) {
2695 purple_privacy_deny(account
, name
, FALSE
, FALSE
);
2697 purple_privacy_allow(account
, name
, FALSE
, FALSE
);
2703 block_select(GntMenuItem
*item
, gpointer n
)
2705 PurpleRequestFields
*fields
;
2706 PurpleRequestFieldGroup
*group
;
2707 PurpleRequestField
*field
;
2709 fields
= purple_request_fields_new();
2711 group
= purple_request_field_group_new(NULL
);
2712 purple_request_fields_add_group(fields
, group
);
2714 field
= purple_request_field_string_new("screenname", _("Name"), NULL
, FALSE
);
2715 purple_request_field_set_type_hint(field
, "screenname");
2716 purple_request_field_set_required(field
, TRUE
);
2717 purple_request_field_group_add_field(group
, field
);
2719 field
= purple_request_field_account_new("account", _("Account"), NULL
);
2720 purple_request_field_set_type_hint(field
, "account");
2721 purple_request_field_set_visible(field
,
2722 (purple_connections_get_all() != NULL
&&
2723 purple_connections_get_all()->next
!= NULL
));
2724 purple_request_field_set_required(field
, TRUE
);
2725 purple_request_field_group_add_field(group
, field
);
2727 field
= purple_request_field_choice_new("block", _("Block/Unblock"), 1);
2728 purple_request_field_choice_add(field
, _("Block"));
2729 purple_request_field_choice_add(field
, _("Unblock"));
2730 purple_request_field_group_add_field(group
, field
);
2732 purple_request_fields(purple_get_blist(), _("Block/Unblock"),
2734 _("Please enter the username or alias of the person "
2735 "you would like to Block/Unblock."),
2737 _("OK"), G_CALLBACK(block_select_cb
),
2743 /* send_im_select* -- Xerox */
2745 send_im_select_cb(gpointer data
, PurpleRequestFields
*fields
)
2747 PurpleAccount
*account
;
2748 const char *username
;
2749 PurpleConversation
*conv
;
2751 account
= purple_request_fields_get_account(fields
, "account");
2752 username
= purple_request_fields_get_string(fields
, "screenname");
2754 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, account
, username
);
2755 purple_conversation_present(conv
);
2759 send_im_select(GntMenuItem
*item
, gpointer n
)
2761 PurpleRequestFields
*fields
;
2762 PurpleRequestFieldGroup
*group
;
2763 PurpleRequestField
*field
;
2765 fields
= purple_request_fields_new();
2767 group
= purple_request_field_group_new(NULL
);
2768 purple_request_fields_add_group(fields
, group
);
2770 field
= purple_request_field_string_new("screenname", _("Name"), NULL
, FALSE
);
2771 purple_request_field_set_type_hint(field
, "screenname");
2772 purple_request_field_set_required(field
, TRUE
);
2773 purple_request_field_group_add_field(group
, field
);
2775 field
= purple_request_field_account_new("account", _("Account"), NULL
);
2776 purple_request_field_set_type_hint(field
, "account");
2777 purple_request_field_set_visible(field
,
2778 (purple_connections_get_all() != NULL
&&
2779 purple_connections_get_all()->next
!= NULL
));
2780 purple_request_field_set_required(field
, TRUE
);
2781 purple_request_field_group_add_field(group
, field
);
2783 purple_request_fields(purple_get_blist(), _("New Instant Message"),
2785 _("Please enter the username or alias of the person "
2786 "you would like to IM."),
2788 _("OK"), G_CALLBACK(send_im_select_cb
),
2795 join_chat_select_cb(gpointer data
, PurpleRequestFields
*fields
)
2797 PurpleAccount
*account
;
2799 PurpleConnection
*gc
;
2801 GHashTable
*hash
= NULL
;
2802 PurpleConversation
*conv
;
2804 account
= purple_request_fields_get_account(fields
, "account");
2805 name
= purple_request_fields_get_string(fields
, "chat");
2807 if (!purple_account_is_connected(account
))
2810 gc
= purple_account_get_connection(account
);
2811 /* Create a new conversation now. This will give focus to the new window.
2812 * But it's necessary to pretend that we left the chat, because otherwise
2813 * a new conversation window will pop up when we finally join the chat. */
2814 if (!(conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT
, name
, account
))) {
2815 conv
= purple_conversation_new(PURPLE_CONV_TYPE_CHAT
, account
, name
);
2816 purple_conv_chat_left(PURPLE_CONV_CHAT(conv
));
2818 purple_conversation_present(conv
);
2821 chat
= purple_blist_find_chat(account
, name
);
2823 PurplePluginProtocolInfo
*info
= PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc
));
2824 if (info
->chat_info_defaults
!= NULL
)
2825 hash
= info
->chat_info_defaults(gc
, name
);
2827 hash
= purple_chat_get_components(chat
);
2829 serv_join_chat(gc
, hash
);
2830 if (chat
== NULL
&& hash
!= NULL
)
2831 g_hash_table_destroy(hash
);
2835 join_chat_select(GntMenuItem
*item
, gpointer n
)
2837 PurpleRequestFields
*fields
;
2838 PurpleRequestFieldGroup
*group
;
2839 PurpleRequestField
*field
;
2841 fields
= purple_request_fields_new();
2843 group
= purple_request_field_group_new(NULL
);
2844 purple_request_fields_add_group(fields
, group
);
2846 field
= purple_request_field_string_new("chat", _("Channel"), NULL
, FALSE
);
2847 purple_request_field_set_required(field
, TRUE
);
2848 purple_request_field_group_add_field(group
, field
);
2850 field
= purple_request_field_account_new("account", _("Account"), NULL
);
2851 purple_request_field_set_type_hint(field
, "account");
2852 purple_request_field_set_visible(field
,
2853 (purple_connections_get_all() != NULL
&&
2854 purple_connections_get_all()->next
!= NULL
));
2855 purple_request_field_set_required(field
, TRUE
);
2856 purple_request_field_group_add_field(group
, field
);
2858 purple_request_fields(purple_get_blist(), _("Join a Chat"),
2860 _("Please enter the name of the chat you want to join."),
2862 _("Join"), G_CALLBACK(join_chat_select_cb
),
2869 view_log_select_cb(gpointer data
, PurpleRequestFields
*fields
)
2871 PurpleAccount
*account
;
2874 PurpleContact
*contact
;
2876 account
= purple_request_fields_get_account(fields
, "account");
2877 name
= purple_request_fields_get_string(fields
, "screenname");
2879 buddy
= purple_find_buddy(account
, name
);
2881 contact
= purple_buddy_get_contact(buddy
);
2887 finch_log_show_contact(contact
);
2889 finch_log_show(PURPLE_LOG_IM
, name
, account
);
2894 view_log_cb(GntMenuItem
*item
, gpointer n
)
2896 PurpleRequestFields
*fields
;
2897 PurpleRequestFieldGroup
*group
;
2898 PurpleRequestField
*field
;
2900 fields
= purple_request_fields_new();
2902 group
= purple_request_field_group_new(NULL
);
2903 purple_request_fields_add_group(fields
, group
);
2905 field
= purple_request_field_string_new("screenname", _("Name"), NULL
, FALSE
);
2906 purple_request_field_set_type_hint(field
, "screenname-all");
2907 purple_request_field_set_required(field
, TRUE
);
2908 purple_request_field_group_add_field(group
, field
);
2910 field
= purple_request_field_account_new("account", _("Account"), NULL
);
2911 purple_request_field_set_type_hint(field
, "account");
2912 purple_request_field_set_visible(field
,
2913 (purple_accounts_get_all() != NULL
&&
2914 purple_accounts_get_all()->next
!= NULL
));
2915 purple_request_field_set_required(field
, TRUE
);
2916 purple_request_field_group_add_field(group
, field
);
2917 purple_request_field_account_set_show_all(field
, TRUE
);
2919 purple_request_fields(purple_get_blist(), _("View Log"),
2921 _("Please enter the username or alias of the person "
2922 "whose log you would like to view."),
2924 _("OK"), G_CALLBACK(view_log_select_cb
),
2931 view_all_logs_cb(GntMenuItem
*item
, gpointer n
)
2933 finch_log_show(PURPLE_LOG_IM
, NULL
, NULL
);
2937 menu_add_buddy_cb(GntMenuItem
*item
, gpointer null
)
2939 purple_blist_request_add_buddy(NULL
, NULL
, NULL
, NULL
);
2943 menu_add_chat_cb(GntMenuItem
*item
, gpointer null
)
2945 purple_blist_request_add_chat(NULL
, NULL
, NULL
, NULL
);
2949 menu_add_group_cb(GntMenuItem
*item
, gpointer null
)
2951 purple_blist_request_add_group();
2955 menu_group_set_cb(GntMenuItem
*item
, gpointer null
)
2957 const char *id
= g_object_get_data(G_OBJECT(item
), "grouping-id");
2958 purple_prefs_set_string(PREF_ROOT
"/grouping", id
);
2964 GntWidget
*menu
, *sub
, *subsub
;
2971 window
= GNT_WINDOW(ggblist
->window
);
2972 ggblist
->menu
= menu
= gnt_menu_new(GNT_MENU_TOPLEVEL
);
2973 gnt_window_set_menu(window
, GNT_MENU(menu
));
2975 item
= gnt_menuitem_new(_("Options"));
2976 gnt_menu_add_item(GNT_MENU(menu
), item
);
2978 sub
= gnt_menu_new(GNT_MENU_POPUP
);
2979 gnt_menuitem_set_submenu(item
, GNT_MENU(sub
));
2981 item
= gnt_menuitem_new(_("Send IM..."));
2982 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "send-im");
2983 gnt_menu_add_item(GNT_MENU(sub
), item
);
2984 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), send_im_select
, NULL
);
2986 item
= gnt_menuitem_new(_("Block/Unblock..."));
2987 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "block-unblock");
2988 gnt_menu_add_item(GNT_MENU(sub
), item
);
2989 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), block_select
, NULL
);
2991 item
= gnt_menuitem_new(_("Join Chat..."));
2992 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "join-chat");
2993 gnt_menu_add_item(GNT_MENU(sub
), item
);
2994 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), join_chat_select
, NULL
);
2996 item
= gnt_menuitem_new(_("View Log..."));
2997 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "view-log");
2998 gnt_menu_add_item(GNT_MENU(sub
), item
);
2999 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), view_log_cb
, NULL
);
3001 item
= gnt_menuitem_new(_("View All Logs"));
3002 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "view-all-logs");
3003 gnt_menu_add_item(GNT_MENU(sub
), item
);
3004 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), view_all_logs_cb
, NULL
);
3006 item
= gnt_menuitem_new(_("Show"));
3007 gnt_menu_add_item(GNT_MENU(sub
), item
);
3008 subsub
= gnt_menu_new(GNT_MENU_POPUP
);
3009 gnt_menuitem_set_submenu(item
, GNT_MENU(subsub
));
3011 item
= gnt_menuitem_check_new(_("Empty groups"));
3012 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "show-empty-groups");
3013 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item
),
3014 purple_prefs_get_bool(PREF_ROOT
"/emptygroups"));
3015 gnt_menu_add_item(GNT_MENU(subsub
), item
);
3016 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), toggle_pref_cb
, PREF_ROOT
"/emptygroups");
3018 item
= gnt_menuitem_check_new(_("Offline buddies"));
3019 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "show-offline-buddies");
3020 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item
),
3021 purple_prefs_get_bool(PREF_ROOT
"/showoffline"));
3022 gnt_menu_add_item(GNT_MENU(subsub
), item
);
3023 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), toggle_pref_cb
, PREF_ROOT
"/showoffline");
3025 item
= gnt_menuitem_new(_("Sort"));
3026 gnt_menu_add_item(GNT_MENU(sub
), item
);
3027 subsub
= gnt_menu_new(GNT_MENU_POPUP
);
3028 gnt_menuitem_set_submenu(item
, GNT_MENU(subsub
));
3030 item
= gnt_menuitem_new(_("By Status"));
3031 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "sort-status");
3032 gnt_menu_add_item(GNT_MENU(subsub
), item
);
3033 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), sort_blist_change_cb
, "status");
3035 item
= gnt_menuitem_new(_("Alphabetically"));
3036 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "sort-alpha");
3037 gnt_menu_add_item(GNT_MENU(subsub
), item
);
3038 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), sort_blist_change_cb
, "text");
3040 item
= gnt_menuitem_new(_("By Log Size"));
3041 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "sort-log");
3042 gnt_menu_add_item(GNT_MENU(subsub
), item
);
3043 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), sort_blist_change_cb
, "log");
3045 item
= gnt_menuitem_new(_("Add"));
3046 gnt_menu_add_item(GNT_MENU(sub
), item
);
3048 subsub
= gnt_menu_new(GNT_MENU_POPUP
);
3049 gnt_menuitem_set_submenu(item
, GNT_MENU(subsub
));
3051 item
= gnt_menuitem_new(_("Buddy"));
3052 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "add-buddy");
3053 gnt_menu_add_item(GNT_MENU(subsub
), item
);
3054 gnt_menuitem_set_callback(item
, menu_add_buddy_cb
, NULL
);
3056 item
= gnt_menuitem_new(_("Chat"));
3057 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "add-chat");
3058 gnt_menu_add_item(GNT_MENU(subsub
), item
);
3059 gnt_menuitem_set_callback(item
, menu_add_chat_cb
, NULL
);
3061 item
= gnt_menuitem_new(_("Group"));
3062 gnt_menuitem_set_id(GNT_MENU_ITEM(item
), "add-group");
3063 gnt_menu_add_item(GNT_MENU(subsub
), item
);
3064 gnt_menuitem_set_callback(item
, menu_add_group_cb
, NULL
);
3066 ggblist
->grouping
= item
= gnt_menuitem_new(_("Grouping"));
3067 gnt_menu_add_item(GNT_MENU(sub
), item
);
3068 reconstruct_grouping_menu();
3070 reconstruct_accounts_menu();
3071 gnt_menu_add_item(GNT_MENU(menu
), ggblist
->accounts
);
3073 reconstruct_plugins_menu();
3074 gnt_menu_add_item(GNT_MENU(menu
), ggblist
->plugins
);
3077 void finch_blist_show()
3079 blist_show(purple_get_blist());
3083 group_collapsed(GntWidget
*widget
, PurpleBlistNode
*node
, gboolean collapsed
, gpointer null
)
3085 if (PURPLE_BLIST_NODE_IS_GROUP(node
))
3086 purple_blist_node_set_bool(node
, "collapsed", collapsed
);
3090 blist_show(PurpleBuddyList
*list
)
3092 if (ggblist
== NULL
)
3094 else if (ggblist
->window
) {
3095 gnt_window_present(ggblist
->window
);
3099 ggblist
->window
= gnt_vwindow_new(FALSE
);
3100 gnt_widget_set_name(ggblist
->window
, "buddylist");
3101 gnt_box_set_toplevel(GNT_BOX(ggblist
->window
), TRUE
);
3102 gnt_box_set_title(GNT_BOX(ggblist
->window
), _("Buddy List"));
3103 gnt_box_set_pad(GNT_BOX(ggblist
->window
), 0);
3105 ggblist
->tree
= gnt_tree_new();
3107 GNT_WIDGET_SET_FLAGS(ggblist
->tree
, GNT_WIDGET_NO_BORDER
);
3108 gnt_widget_set_size(ggblist
->tree
, purple_prefs_get_int(PREF_ROOT
"/size/width"),
3109 purple_prefs_get_int(PREF_ROOT
"/size/height"));
3110 gnt_widget_set_position(ggblist
->window
, purple_prefs_get_int(PREF_ROOT
"/position/x"),
3111 purple_prefs_get_int(PREF_ROOT
"/position/y"));
3113 gnt_box_add_widget(GNT_BOX(ggblist
->window
), ggblist
->tree
);
3115 ggblist
->status
= gnt_combo_box_new();
3116 gnt_box_add_widget(GNT_BOX(ggblist
->window
), ggblist
->status
);
3117 ggblist
->statustext
= gnt_entry_new(NULL
);
3118 gnt_box_add_widget(GNT_BOX(ggblist
->window
), ggblist
->statustext
);
3120 gnt_widget_show(ggblist
->window
);
3122 purple_signal_connect(purple_connections_get_handle(), "signed-on", finch_blist_get_handle(),
3123 PURPLE_CALLBACK(reconstruct_accounts_menu
), NULL
);
3124 purple_signal_connect(purple_connections_get_handle(), "signed-off", finch_blist_get_handle(),
3125 PURPLE_CALLBACK(reconstruct_accounts_menu
), NULL
);
3126 purple_signal_connect(purple_accounts_get_handle(), "account-actions-changed", finch_blist_get_handle(),
3127 PURPLE_CALLBACK(reconstruct_accounts_menu
), NULL
);
3128 purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", finch_blist_get_handle(),
3129 PURPLE_CALLBACK(buddy_status_changed
), ggblist
);
3130 purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", finch_blist_get_handle(),
3131 PURPLE_CALLBACK(buddy_idle_changed
), ggblist
);
3133 purple_signal_connect(purple_plugins_get_handle(), "plugin-load", finch_blist_get_handle(),
3134 PURPLE_CALLBACK(reconstruct_plugins_menu
), NULL
);
3135 purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", finch_blist_get_handle(),
3136 PURPLE_CALLBACK(reconstruct_plugins_menu
), NULL
);
3138 purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_blist_get_handle(),
3139 PURPLE_CALLBACK(buddy_signed_on_off
), ggblist
);
3140 purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_blist_get_handle(),
3141 PURPLE_CALLBACK(buddy_signed_on_off
), ggblist
);
3144 /* These I plan to use to indicate unread-messages etc. */
3145 purple_signal_connect(purple_conversations_get_handle(), "received-im-msg", finch_blist_get_handle(),
3146 PURPLE_CALLBACK(received_im_msg
), list
);
3147 purple_signal_connect(purple_conversations_get_handle(), "sent-im-msg", finch_blist_get_handle(),
3148 PURPLE_CALLBACK(sent_im_msg
), NULL
);
3150 purple_signal_connect(purple_conversations_get_handle(), "received-chat-msg", finch_blist_get_handle(),
3151 PURPLE_CALLBACK(received_chat_msg
), list
);
3154 g_signal_connect(G_OBJECT(ggblist
->tree
), "selection_changed", G_CALLBACK(selection_changed
), ggblist
);
3155 g_signal_connect(G_OBJECT(ggblist
->tree
), "key_pressed", G_CALLBACK(key_pressed
), ggblist
);
3156 g_signal_connect(G_OBJECT(ggblist
->tree
), "context-menu", G_CALLBACK(context_menu
), ggblist
);
3157 g_signal_connect(G_OBJECT(ggblist
->tree
), "collapse-toggled", G_CALLBACK(group_collapsed
), NULL
);
3158 g_signal_connect(G_OBJECT(ggblist
->tree
), "activate", G_CALLBACK(selection_activate
), ggblist
);
3159 g_signal_connect_data(G_OBJECT(ggblist
->tree
), "gained-focus", G_CALLBACK(draw_tooltip
),
3160 ggblist
, 0, G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
3161 g_signal_connect_data(G_OBJECT(ggblist
->tree
), "lost-focus", G_CALLBACK(remove_peripherals
),
3162 ggblist
, 0, G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
3163 g_signal_connect_data(G_OBJECT(ggblist
->window
), "workspace-hidden", G_CALLBACK(remove_peripherals
),
3164 ggblist
, 0, G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
3165 g_signal_connect(G_OBJECT(ggblist
->tree
), "size_changed", G_CALLBACK(size_changed_cb
), NULL
);
3166 g_signal_connect(G_OBJECT(ggblist
->window
), "position_set", G_CALLBACK(save_position_cb
), NULL
);
3167 g_signal_connect(G_OBJECT(ggblist
->window
), "destroy", G_CALLBACK(reset_blist_window
), NULL
);
3169 /* Status signals */
3170 purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-changed", finch_blist_get_handle(),
3171 PURPLE_CALLBACK(savedstatus_changed
), NULL
);
3172 g_signal_connect(G_OBJECT(ggblist
->status
), "selection_changed",
3173 G_CALLBACK(status_selection_changed
), NULL
);
3174 g_signal_connect(G_OBJECT(ggblist
->statustext
), "key_pressed",
3175 G_CALLBACK(status_text_changed
), NULL
);
3179 populate_buddylist();
3181 savedstatus_changed(purple_savedstatus_get_current(), NULL
);
3184 void finch_blist_uninit()
3186 if (ggblist
== NULL
)
3189 gnt_widget_destroy(ggblist
->window
);
3194 gboolean
finch_blist_get_position(int *x
, int *y
)
3196 if (!ggblist
|| !ggblist
->window
)
3198 gnt_widget_get_position(ggblist
->window
, x
, y
);
3202 void finch_blist_set_position(int x
, int y
)
3204 gnt_widget_set_position(ggblist
->window
, x
, y
);
3207 gboolean
finch_blist_get_size(int *width
, int *height
)
3209 if (!ggblist
|| !ggblist
->window
)
3211 gnt_widget_get_size(ggblist
->window
, width
, height
);
3215 void finch_blist_set_size(int width
, int height
)
3217 gnt_widget_set_size(ggblist
->window
, width
, height
);
3220 void finch_blist_install_manager(const FinchBlistManager
*manager
)
3222 if (!g_list_find(managers
, manager
)) {
3223 managers
= g_list_append(managers
, (gpointer
)manager
);
3224 reconstruct_grouping_menu();
3225 if (strcmp(manager
->id
, purple_prefs_get_string(PREF_ROOT
"/grouping")) == 0)
3226 purple_prefs_trigger_callback(PREF_ROOT
"/grouping");
3230 void finch_blist_uninstall_manager(const FinchBlistManager
*manager
)
3232 if (g_list_find(managers
, manager
)) {
3233 managers
= g_list_remove(managers
, manager
);
3234 reconstruct_grouping_menu();
3235 if (strcmp(manager
->id
, purple_prefs_get_string(PREF_ROOT
"/grouping")) == 0)
3236 purple_prefs_trigger_callback(PREF_ROOT
"/grouping");
3240 FinchBlistManager
* finch_blist_manager_find(const char *id
)
3242 GList
*iter
= managers
;
3246 for (; iter
; iter
= iter
->next
) {
3247 FinchBlistManager
*m
= iter
->data
;
3248 if (strcmp(id
, m
->id
) == 0)
3254 GntTree
* finch_blist_get_tree(void)
3256 return ggblist
? GNT_TREE(ggblist
->tree
) : NULL
;