2 * @file gntblist.c GNT BuddyList API
7 * Finch is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include <savedstatuses.h>
38 #include "gntcombobox.h"
44 #include "gntmenuitem.h"
45 #include "gntmenuitemcheck.h"
46 #include "gntpounce.h"
49 #include "gntwindow.h"
53 #include "gntstatus.h"
56 #define PREF_ROOT "/finch/blist"
57 #define TYPING_TIMEOUT 4000
65 PurpleBlistNode
*tnode
; /* Who is the tooltip being displayed for? */
66 GList
*tagged
; /* A list of tagged blistnodes */
69 PurpleBlistNode
*cnode
;
71 /* XXX: I am KISSing */
72 GntWidget
*status
; /* Dropdown with the statuses */
73 GntWidget
*statustext
; /* Status message */
77 /* These are the menuitems that get regenerated */
78 GntMenuItem
*accounts
;
95 PurpleStatusPrimitive prim
;
96 PurpleSavedStatus
*saved
;
102 static void add_buddy(PurpleBuddy
*buddy
, FinchBlist
*ggblist
);
103 static void add_contact(PurpleContact
*contact
, FinchBlist
*ggblist
);
104 static void add_group(PurpleGroup
*group
, FinchBlist
*ggblist
);
105 static void add_chat(PurpleChat
*chat
, FinchBlist
*ggblist
);
106 static void add_node(PurpleBlistNode
*node
, FinchBlist
*ggblist
);
107 static void draw_tooltip(FinchBlist
*ggblist
);
108 static gboolean
remove_typing_cb(gpointer null
);
109 static void remove_peripherals(FinchBlist
*ggblist
);
110 static const char * get_display_name(PurpleBlistNode
*node
);
111 static void savedstatus_changed(PurpleSavedStatus
*now
, PurpleSavedStatus
*old
);
112 static void blist_show(PurpleBuddyList
*list
);
113 static void update_node_display(PurpleBlistNode
*buddy
, FinchBlist
*ggblist
);
114 static void update_buddy_display(PurpleBuddy
*buddy
, FinchBlist
*ggblist
);
115 static void account_signed_on_cb(PurpleConnection
*pc
, gpointer null
);
118 static int blist_node_compare_position(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
119 static int blist_node_compare_text(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
120 static int blist_node_compare_status(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
121 static int blist_node_compare_log(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
);
124 is_contact_online(PurpleContact
*contact
)
126 PurpleBlistNode
*node
;
127 for (node
= ((PurpleBlistNode
*)contact
)->child
; node
; node
= node
->next
) {
128 if (PURPLE_BUDDY_IS_ONLINE((PurpleBuddy
*)node
))
135 is_group_online(PurpleGroup
*group
)
137 PurpleBlistNode
*node
;
138 for (node
= ((PurpleBlistNode
*)group
)->child
; node
; node
= node
->next
) {
139 if (PURPLE_BLIST_NODE_IS_CHAT(node
))
141 else if (is_contact_online((PurpleContact
*)node
))
148 new_node(PurpleBlistNode
*node
)
152 static void add_node(PurpleBlistNode
*node
, FinchBlist
*ggblist
)
154 if (PURPLE_BLIST_NODE_IS_BUDDY(node
))
155 add_buddy((PurpleBuddy
*)node
, ggblist
);
156 else if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
157 add_contact((PurpleContact
*)node
, ggblist
);
158 else if (PURPLE_BLIST_NODE_IS_GROUP(node
))
159 add_group((PurpleGroup
*)node
, ggblist
);
160 else if (PURPLE_BLIST_NODE_IS_CHAT(node
))
161 add_chat((PurpleChat
*)node
, ggblist
);
162 draw_tooltip(ggblist
);
166 remove_tooltip(FinchBlist
*ggblist
)
168 gnt_widget_destroy(ggblist
->tooltip
);
169 ggblist
->tooltip
= NULL
;
170 ggblist
->tnode
= NULL
;
174 node_remove(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
176 FinchBlist
*ggblist
= list
->ui_data
;
178 if (ggblist
== NULL
|| node
->ui_data
== NULL
)
181 gnt_tree_remove(GNT_TREE(ggblist
->tree
), node
);
182 node
->ui_data
= NULL
;
184 ggblist
->tagged
= g_list_remove(ggblist
->tagged
, node
);
186 if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
187 PurpleContact
*contact
= (PurpleContact
*)node
->parent
;
188 if ((!purple_prefs_get_bool(PREF_ROOT
"/showoffline") && !is_contact_online(contact
)) ||
189 contact
->currentsize
< 1)
190 node_remove(list
, (PurpleBlistNode
*)contact
);
191 } else if (!PURPLE_BLIST_NODE_IS_GROUP(node
)) {
192 PurpleGroup
*group
= (PurpleGroup
*)node
->parent
;
193 if ((!purple_prefs_get_bool(PREF_ROOT
"/showoffline") && !is_group_online(group
)) ||
194 group
->currentsize
< 1)
195 node_remove(list
, node
->parent
);
196 for (node
= node
->child
; node
; node
= node
->next
)
197 node
->ui_data
= NULL
;
199 for (node
= node
->child
; node
; node
= node
->next
)
200 node_remove(list
, node
);
203 draw_tooltip(ggblist
);
207 node_update(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
209 /* It really looks like this should never happen ... but it does.
210 This will at least emit a warning to the log when it
211 happens, so maybe someone will figure it out. */
212 g_return_if_fail(node
!= NULL
);
214 if (list
->ui_data
== NULL
)
215 return; /* XXX: this is probably the place to auto-join chats */
217 if (node
->ui_data
!= NULL
) {
218 gnt_tree_change_text(GNT_TREE(ggblist
->tree
), node
,
219 0, get_display_name(node
));
220 gnt_tree_sort_row(GNT_TREE(ggblist
->tree
), node
);
223 if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
224 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
225 if (purple_account_is_connected(buddy
->account
) &&
226 (PURPLE_BUDDY_IS_ONLINE(buddy
) || purple_prefs_get_bool(PREF_ROOT
"/showoffline")))
227 add_node((PurpleBlistNode
*)buddy
, list
->ui_data
);
229 node_remove(purple_get_blist(), node
);
231 node_update(list
, node
->parent
);
232 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
233 add_chat((PurpleChat
*)node
, list
->ui_data
);
234 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
235 PurpleContact
*contact
= (PurpleContact
*)node
;
236 if ((!purple_prefs_get_bool(PREF_ROOT
"/showoffline") && !is_contact_online(contact
)) ||
237 contact
->currentsize
< 1)
238 node_remove(purple_get_blist(), node
);
240 if (node
->ui_data
== NULL
) {
241 /* The core seems to expect the UI to add the buddies. */
242 for (node
= node
->child
; node
; node
= node
->next
)
243 add_node(node
, list
->ui_data
);
246 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
247 PurpleGroup
*group
= (PurpleGroup
*)node
;
248 if ((!purple_prefs_get_bool(PREF_ROOT
"/showoffline") && !is_group_online(group
)) ||
249 group
->currentsize
< 1)
250 node_remove(list
, node
);
252 add_node(node
, list
->ui_data
);
257 new_list(PurpleBuddyList
*list
)
262 ggblist
= g_new0(FinchBlist
, 1);
263 list
->ui_data
= ggblist
;
267 add_buddy_cb(void *data
, PurpleRequestFields
*allfields
)
269 const char *username
= purple_request_fields_get_string(allfields
, "screenname");
270 const char *alias
= purple_request_fields_get_string(allfields
, "alias");
271 const char *group
= purple_request_fields_get_string(allfields
, "group");
272 PurpleAccount
*account
= purple_request_fields_get_account(allfields
, "account");
273 const char *error
= NULL
;
278 error
= _("You must provide a screename for the buddy.");
280 error
= _("You must provide a group.");
282 error
= _("You must select an account.");
286 purple_notify_error(NULL
, _("Error"), _("Error adding buddy"), error
);
290 grp
= purple_find_group(group
);
293 grp
= purple_group_new(group
);
294 purple_blist_add_group(grp
, NULL
);
297 buddy
= purple_buddy_new(account
, username
, alias
);
298 purple_blist_add_buddy(buddy
, NULL
, grp
, NULL
);
299 purple_account_add_buddy(account
, buddy
);
303 finch_request_add_buddy(PurpleAccount
*account
, const char *username
, const char *grp
, const char *alias
)
305 PurpleRequestFields
*fields
= purple_request_fields_new();
306 PurpleRequestFieldGroup
*group
= purple_request_field_group_new(NULL
);
307 PurpleRequestField
*field
;
309 purple_request_fields_add_group(fields
, group
);
311 field
= purple_request_field_string_new("screenname", _("Screen Name"), username
, FALSE
);
312 purple_request_field_group_add_field(group
, field
);
314 field
= purple_request_field_string_new("alias", _("Alias"), alias
, FALSE
);
315 purple_request_field_group_add_field(group
, field
);
317 field
= purple_request_field_string_new("group", _("Group"), grp
, FALSE
);
318 purple_request_field_group_add_field(group
, field
);
319 purple_request_field_set_type_hint(field
, "group");
321 field
= purple_request_field_account_new("account", _("Account"), NULL
);
322 purple_request_field_account_set_show_all(field
, FALSE
);
324 purple_request_field_account_set_value(field
, account
);
325 purple_request_field_group_add_field(group
, field
);
327 purple_request_fields(NULL
, _("Add Buddy"), NULL
, _("Please enter buddy information."),
329 _("Add"), G_CALLBACK(add_buddy_cb
),
336 add_chat_cb(void *data
, PurpleRequestFields
*allfields
)
338 PurpleAccount
*account
;
339 const char *alias
, *name
, *group
;
342 GHashTable
*hash
= NULL
;
343 PurpleConnection
*gc
;
345 account
= purple_request_fields_get_account(allfields
, "account");
346 name
= purple_request_fields_get_string(allfields
, "name");
347 alias
= purple_request_fields_get_string(allfields
, "alias");
348 group
= purple_request_fields_get_string(allfields
, "group");
350 if (!purple_account_is_connected(account
) || !name
|| !*name
)
353 if (!group
|| !*group
)
356 gc
= purple_account_get_connection(account
);
358 if (PURPLE_PLUGIN_PROTOCOL_INFO(gc
->prpl
)->chat_info_defaults
!= NULL
)
359 hash
= PURPLE_PLUGIN_PROTOCOL_INFO(gc
->prpl
)->chat_info_defaults(gc
, name
);
361 chat
= purple_chat_new(account
, name
, hash
);
364 if ((grp
= purple_find_group(group
)) == NULL
) {
365 grp
= purple_group_new(group
);
366 purple_blist_add_group(grp
, NULL
);
368 purple_blist_add_chat(chat
, grp
, NULL
);
369 purple_blist_alias_chat(chat
, alias
);
374 finch_request_add_chat(PurpleAccount
*account
, PurpleGroup
*grp
, const char *alias
, const char *name
)
376 PurpleRequestFields
*fields
= purple_request_fields_new();
377 PurpleRequestFieldGroup
*group
= purple_request_field_group_new(NULL
);
378 PurpleRequestField
*field
;
380 purple_request_fields_add_group(fields
, group
);
382 field
= purple_request_field_account_new("account", _("Account"), NULL
);
383 purple_request_field_account_set_show_all(field
, FALSE
);
385 purple_request_field_account_set_value(field
, account
);
386 purple_request_field_group_add_field(group
, field
);
388 field
= purple_request_field_string_new("name", _("Name"), name
, FALSE
);
389 purple_request_field_group_add_field(group
, field
);
391 field
= purple_request_field_string_new("alias", _("Alias"), alias
, FALSE
);
392 purple_request_field_group_add_field(group
, field
);
394 field
= purple_request_field_string_new("group", _("Group"), grp
? grp
->name
: NULL
, FALSE
);
395 purple_request_field_group_add_field(group
, field
);
397 purple_request_fields(NULL
, _("Add Chat"), NULL
,
398 _("You can edit more information from the context menu later."),
399 fields
, _("Add"), G_CALLBACK(add_chat_cb
), _("Cancel"), NULL
,
405 add_group_cb(gpointer null
, const char *group
)
409 if (!group
|| !*group
)
411 purple_notify_error(NULL
, _("Error"), _("Error adding group"),
412 _("You must give a name for the group to add."));
416 grp
= purple_find_group(group
);
419 grp
= purple_group_new(group
);
420 purple_blist_add_group(grp
, NULL
);
424 purple_notify_error(NULL
, _("Error"), _("Error adding group"),
425 _("A group with the name already exists."));
430 finch_request_add_group()
432 purple_request_input(NULL
, _("Add Group"), NULL
, _("Enter the name of the group"),
433 NULL
, FALSE
, FALSE
, NULL
,
434 _("Add"), G_CALLBACK(add_group_cb
), _("Cancel"), NULL
,
439 static PurpleBlistUiOps blist_ui_ops
=
448 .request_add_buddy
= finch_request_add_buddy
,
449 .request_add_chat
= finch_request_add_chat
,
450 .request_add_group
= finch_request_add_group
454 finch_blist_get_handle()
462 add_group(PurpleGroup
*group
, FinchBlist
*ggblist
)
464 PurpleBlistNode
*node
= (PurpleBlistNode
*)group
;
467 node
->ui_data
= gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), group
,
468 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), get_display_name(node
)), NULL
, NULL
);
469 gnt_tree_set_expanded(GNT_TREE(ggblist
->tree
), node
,
470 !purple_blist_node_get_bool(node
, "collapsed"));
474 get_display_name(PurpleBlistNode
*node
)
476 static char text
[2096];
477 char status
[8] = " ";
478 const char *name
= NULL
;
480 if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
481 node
= (PurpleBlistNode
*)purple_contact_get_priority_buddy((PurpleContact
*)node
); /* XXX: this can return NULL?! */
486 if (PURPLE_BLIST_NODE_IS_BUDDY(node
))
488 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
489 PurpleStatusPrimitive prim
;
490 PurplePresence
*presence
;
492 gboolean ascii
= gnt_ascii_only();
494 presence
= purple_buddy_get_presence(buddy
);
495 now
= purple_presence_get_active_status(presence
);
497 prim
= purple_status_type_get_primitive(purple_status_get_type(now
));
501 case PURPLE_STATUS_OFFLINE
:
502 strncpy(status
, ascii
? "x" : "⊗", sizeof(status
) - 1);
504 case PURPLE_STATUS_AVAILABLE
:
505 strncpy(status
, ascii
? "o" : "â—¯", sizeof(status
) - 1);
508 strncpy(status
, ascii
? "." : "⊖", sizeof(status
) - 1);
511 name
= purple_buddy_get_alias(buddy
);
513 else if (PURPLE_BLIST_NODE_IS_CHAT(node
))
515 PurpleChat
*chat
= (PurpleChat
*)node
;
516 name
= purple_chat_get_name(chat
);
518 strncpy(status
, "~", sizeof(status
) - 1);
520 else if (PURPLE_BLIST_NODE_IS_GROUP(node
))
521 return ((PurpleGroup
*)node
)->name
;
523 snprintf(text
, sizeof(text
) - 1, "%s %s", status
, name
);
529 add_chat(PurpleChat
*chat
, FinchBlist
*ggblist
)
532 PurpleBlistNode
*node
= (PurpleBlistNode
*)chat
;
535 if (!purple_account_is_connected(chat
->account
))
538 group
= purple_chat_get_group(chat
);
539 add_node((PurpleBlistNode
*)group
, ggblist
);
541 node
->ui_data
= gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), chat
,
542 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), get_display_name(node
)),
547 add_contact(PurpleContact
*contact
, FinchBlist
*ggblist
)
550 PurpleBlistNode
*node
= (PurpleBlistNode
*)contact
;
556 name
= get_display_name(node
);
560 group
= (PurpleGroup
*)node
->parent
;
561 add_node((PurpleBlistNode
*)group
, ggblist
);
563 node
->ui_data
= gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), contact
,
564 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), name
),
567 gnt_tree_set_expanded(GNT_TREE(ggblist
->tree
), contact
, FALSE
);
571 add_buddy(PurpleBuddy
*buddy
, FinchBlist
*ggblist
)
573 PurpleContact
*contact
;
574 PurpleBlistNode
*node
= (PurpleBlistNode
*)buddy
;
578 contact
= (PurpleContact
*)node
->parent
;
579 if (!contact
) /* When a new buddy is added and show-offline is set */
581 add_node((PurpleBlistNode
*)contact
, ggblist
);
583 node
->ui_data
= gnt_tree_add_row_after(GNT_TREE(ggblist
->tree
), buddy
,
584 gnt_tree_create_row(GNT_TREE(ggblist
->tree
), get_display_name(node
)),
586 if (purple_presence_is_idle(purple_buddy_get_presence(buddy
))) {
587 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), buddy
, GNT_TEXT_FLAG_DIM
);
588 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), contact
, GNT_TEXT_FLAG_DIM
);
590 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), buddy
, 0);
591 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), contact
, 0);
597 buddy_signed_on(PurpleBuddy
*buddy
, FinchBlist
*ggblist
)
599 add_node((PurpleBlistNode
*)buddy
, ggblist
);
603 buddy_signed_off(PurpleBuddy
*buddy
, FinchBlist
*ggblist
)
605 node_remove(purple_get_blist(), (PurpleBlistNode
*)buddy
);
609 PurpleBlistUiOps
*finch_blist_get_ui_ops()
611 return &blist_ui_ops
;
615 selection_activate(GntWidget
*widget
, FinchBlist
*ggblist
)
617 GntTree
*tree
= GNT_TREE(ggblist
->tree
);
618 PurpleBlistNode
*node
= gnt_tree_get_selection_data(tree
);
623 if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
624 node
= (PurpleBlistNode
*)purple_contact_get_priority_buddy((PurpleContact
*)node
);
626 if (PURPLE_BLIST_NODE_IS_BUDDY(node
))
628 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
629 PurpleConversation
*conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
,
630 purple_buddy_get_account(buddy
),
631 purple_buddy_get_name(buddy
));
632 finch_conversation_set_active(conv
);
634 else if (PURPLE_BLIST_NODE_IS_CHAT(node
))
636 PurpleChat
*chat
= (PurpleChat
*)node
;
637 serv_join_chat(chat
->account
->gc
, chat
->components
);
642 context_menu_callback(GntMenuItem
*item
, gpointer data
)
644 PurpleMenuAction
*action
= data
;
645 PurpleBlistNode
*node
= ggblist
->cnode
;
647 void (*callback
)(PurpleBlistNode
*, gpointer
);
648 callback
= (void (*)(PurpleBlistNode
*, gpointer
))action
->callback
;
650 callback(action
->data
, node
);
657 gnt_append_menu_action(GntMenu
*menu
, PurpleMenuAction
*action
, gpointer parent
)
665 item
= gnt_menuitem_new(action
->label
);
666 if (action
->callback
)
667 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), context_menu_callback
, action
);
668 gnt_menu_add_item(menu
, GNT_MENU_ITEM(item
));
670 if (action
->children
) {
671 GntWidget
*sub
= gnt_menu_new(GNT_MENU_POPUP
);
672 gnt_menuitem_set_submenu(item
, GNT_MENU(sub
));
673 for (list
= action
->children
; list
; list
= list
->next
)
674 gnt_append_menu_action(GNT_MENU(sub
), list
->data
, action
);
679 append_proto_menu(GntMenu
*menu
, PurpleConnection
*gc
, PurpleBlistNode
*node
)
682 PurplePluginProtocolInfo
*prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(gc
->prpl
);
684 if(!prpl_info
|| !prpl_info
->blist_node_menu
)
687 for(list
= prpl_info
->blist_node_menu(node
); list
;
688 list
= g_list_delete_link(list
, list
))
690 PurpleMenuAction
*act
= (PurpleMenuAction
*) list
->data
;
692 gnt_append_menu_action(menu
, act
, NULL
);
697 add_custom_action(GntMenu
*menu
, const char *label
, PurpleCallback callback
,
700 PurpleMenuAction
*action
= purple_menu_action_new(label
, callback
, data
, NULL
);
701 gnt_append_menu_action(menu
, action
, NULL
);
702 g_signal_connect_swapped(G_OBJECT(menu
), "destroy",
703 G_CALLBACK(purple_menu_action_free
), action
);
707 chat_components_edit_ok(PurpleChat
*chat
, PurpleRequestFields
*allfields
)
709 GList
*groups
, *fields
;
711 for (groups
= purple_request_fields_get_groups(allfields
); groups
; groups
= groups
->next
) {
712 fields
= purple_request_field_group_get_fields(groups
->data
);
713 for (; fields
; fields
= fields
->next
) {
714 PurpleRequestField
*field
= fields
->data
;
718 id
= purple_request_field_get_id(field
);
719 if (purple_request_field_get_type(field
) == PURPLE_REQUEST_FIELD_INTEGER
)
720 val
= g_strdup_printf("%d", purple_request_field_int_get_value(field
));
722 val
= g_strdup(purple_request_field_string_get_value(field
));
724 g_hash_table_replace(chat
->components
, g_strdup(id
), val
); /* val should not be free'd */
730 chat_components_edit(PurpleChat
*chat
, PurpleBlistNode
*selected
)
732 PurpleRequestFields
*fields
= purple_request_fields_new();
733 PurpleRequestFieldGroup
*group
= purple_request_field_group_new(NULL
);
734 PurpleRequestField
*field
;
736 struct proto_chat_entry
*pce
;
738 purple_request_fields_add_group(fields
, group
);
740 parts
= PURPLE_PLUGIN_PROTOCOL_INFO(chat
->account
->gc
->prpl
)->chat_info(chat
->account
->gc
);
742 for (iter
= parts
; iter
; iter
= iter
->next
) {
746 const char *str
= g_hash_table_lookup(chat
->components
, pce
->identifier
);
747 if (!str
|| sscanf(str
, "%d", &val
) != 1)
749 field
= purple_request_field_int_new(pce
->identifier
, pce
->label
, val
);
751 field
= purple_request_field_string_new(pce
->identifier
, pce
->label
,
752 g_hash_table_lookup(chat
->components
, pce
->identifier
), FALSE
);
755 purple_request_field_group_add_field(group
, field
);
761 purple_request_fields(NULL
, _("Edit Chat"), NULL
, _("Please Update the necessary fields."),
762 fields
, _("Edit"), G_CALLBACK(chat_components_edit_ok
), _("Cancel"), NULL
,
768 autojoin_toggled(GntMenuItem
*item
, gpointer data
)
770 PurpleMenuAction
*action
= data
;
771 purple_blist_node_set_bool(action
->data
, "gnt-autojoin",
772 gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item
)));
776 create_chat_menu(GntMenu
*menu
, PurpleChat
*chat
)
778 PurpleMenuAction
*action
= purple_menu_action_new(_("Auto-join"), NULL
, chat
, NULL
);
779 GntMenuItem
*check
= gnt_menuitem_check_new(action
->label
);
780 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(check
),
781 purple_blist_node_get_bool((PurpleBlistNode
*)chat
, "gnt-autojoin"));
782 gnt_menu_add_item(menu
, check
);
783 gnt_menuitem_set_callback(check
, autojoin_toggled
, action
);
784 g_signal_connect_swapped(G_OBJECT(menu
), "destroy",
785 G_CALLBACK(purple_menu_action_free
), action
);
787 add_custom_action(menu
, _("Edit Settings"), (PurpleCallback
)chat_components_edit
, chat
);
791 finch_add_buddy(PurpleGroup
*grp
, PurpleBlistNode
*selected
)
793 purple_blist_request_add_buddy(NULL
, NULL
, grp
? grp
->name
: NULL
, NULL
);
797 finch_add_group(PurpleGroup
*grp
, PurpleBlistNode
*selected
)
799 purple_blist_request_add_group();
803 finch_add_chat(PurpleGroup
*grp
, PurpleBlistNode
*selected
)
805 purple_blist_request_add_chat(NULL
, grp
, NULL
, NULL
);
809 create_group_menu(GntMenu
*menu
, PurpleGroup
*group
)
811 add_custom_action(menu
, _("Add Buddy"),
812 PURPLE_CALLBACK(finch_add_buddy
), group
);
813 add_custom_action(menu
, _("Add Chat"),
814 PURPLE_CALLBACK(finch_add_chat
), group
);
815 add_custom_action(menu
, _("Add Group"),
816 PURPLE_CALLBACK(finch_add_group
), group
);
820 finch_blist_get_buddy_info_cb(PurpleBuddy
*buddy
, PurpleBlistNode
*selected
)
822 serv_get_info(buddy
->account
->gc
, purple_buddy_get_name(buddy
));
826 finch_blist_menu_send_file_cb(PurpleBuddy
*buddy
, PurpleBlistNode
*selected
)
828 serv_send_file(buddy
->account
->gc
, buddy
->name
, NULL
);
832 finch_blist_pounce_node_cb(PurpleBlistNode
*node
, PurpleBlistNode
*selected
)
835 if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
836 b
= purple_contact_get_priority_buddy((PurpleContact
*)node
);
838 b
= (PurpleBuddy
*)node
;
839 finch_pounce_editor_show(b
->account
, b
->name
, NULL
);
844 create_buddy_menu(GntMenu
*menu
, PurpleBuddy
*buddy
)
846 PurplePluginProtocolInfo
*prpl_info
;
848 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(buddy
->account
->gc
->prpl
);
849 if (prpl_info
&& prpl_info
->get_info
)
851 add_custom_action(menu
, _("Get Info"),
852 PURPLE_CALLBACK(finch_blist_get_buddy_info_cb
), buddy
);
855 add_custom_action(menu
, _("Add Buddy Pounce"),
856 PURPLE_CALLBACK(finch_blist_pounce_node_cb
), buddy
);
858 if (prpl_info
&& prpl_info
->send_file
)
860 if (!prpl_info
->can_receive_file
||
861 prpl_info
->can_receive_file(buddy
->account
->gc
, buddy
->name
))
862 add_custom_action(menu
, _("Send File"),
863 PURPLE_CALLBACK(finch_blist_menu_send_file_cb
), buddy
);
866 add_custom_action(tree
, _("View Log"),
867 PURPLE_CALLBACK(finch_blist_view_log_cb
)), buddy
);
870 /* Protocol actions */
871 append_proto_menu(menu
,
872 purple_account_get_connection(purple_buddy_get_account(buddy
)),
873 (PurpleBlistNode
*)buddy
);
877 append_extended_menu(GntMenu
*menu
, PurpleBlistNode
*node
)
881 for (iter
= purple_blist_node_get_extended_menu(node
);
882 iter
; iter
= g_list_delete_link(iter
, iter
))
884 gnt_append_menu_action(menu
, iter
->data
, NULL
);
888 /* Xerox'd from gtkdialogs.c:purple_gtkdialogs_remove_contact_cb */
890 remove_contact(PurpleContact
*contact
)
892 PurpleBlistNode
*bnode
, *cnode
;
895 cnode
= (PurpleBlistNode
*)contact
;
896 group
= (PurpleGroup
*)cnode
->parent
;
897 for (bnode
= cnode
->child
; bnode
; bnode
= bnode
->next
) {
898 PurpleBuddy
*buddy
= (PurpleBuddy
*)bnode
;
899 if (purple_account_is_connected(buddy
->account
))
900 purple_account_remove_buddy(buddy
->account
, buddy
, group
);
902 purple_blist_remove_contact(contact
);
906 rename_blist_node(PurpleBlistNode
*node
, const char *newname
)
908 const char *name
= newname
;
912 if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
913 PurpleContact
*contact
= (PurpleContact
*)node
;
914 PurpleBuddy
*buddy
= purple_contact_get_priority_buddy(contact
);
915 purple_blist_alias_contact(contact
, name
);
916 purple_blist_alias_buddy(buddy
, name
);
917 serv_alias_buddy(buddy
);
918 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
919 purple_blist_alias_buddy((PurpleBuddy
*)node
, name
);
920 serv_alias_buddy((PurpleBuddy
*)node
);
921 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
))
922 purple_blist_alias_chat((PurpleChat
*)node
, name
);
923 else if (PURPLE_BLIST_NODE_IS_GROUP(node
) && (name
!= NULL
))
924 purple_blist_rename_group((PurpleGroup
*)node
, name
);
926 g_return_if_reached();
930 finch_blist_rename_node_cb(PurpleBlistNode
*node
, PurpleBlistNode
*selected
)
932 const char *name
= NULL
;
936 if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
937 name
= purple_contact_get_alias((PurpleContact
*)node
);
938 else if (PURPLE_BLIST_NODE_IS_BUDDY(node
))
939 name
= purple_buddy_get_contact_alias((PurpleBuddy
*)node
);
940 else if (PURPLE_BLIST_NODE_IS_CHAT(node
))
941 name
= purple_chat_get_name((PurpleChat
*)node
);
942 else if (PURPLE_BLIST_NODE_IS_GROUP(node
))
943 name
= ((PurpleGroup
*)node
)->name
;
945 g_return_if_reached();
947 prompt
= g_strdup_printf(_("Please enter the new name for %s"), name
);
949 text
= PURPLE_BLIST_NODE_IS_GROUP(node
) ? _("Rename") : _("Alias");
950 purple_request_input(node
, text
, prompt
, _("Enter empty string to reset the name."),
951 name
, FALSE
, FALSE
, NULL
, text
, G_CALLBACK(rename_blist_node
),
959 /* Xeroxed from gtkdialogs.c:purple_gtkdialogs_remove_group_cb*/
961 remove_group(PurpleGroup
*group
)
963 PurpleBlistNode
*cnode
, *bnode
;
965 cnode
= ((PurpleBlistNode
*)group
)->child
;
968 if (PURPLE_BLIST_NODE_IS_CONTACT(cnode
)) {
969 bnode
= cnode
->child
;
973 if (PURPLE_BLIST_NODE_IS_BUDDY(bnode
)) {
974 buddy
= (PurpleBuddy
*)bnode
;
976 if (purple_account_is_connected(buddy
->account
)) {
977 purple_account_remove_buddy(buddy
->account
, buddy
, group
);
978 purple_blist_remove_buddy(buddy
);
984 } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode
)) {
985 PurpleChat
*chat
= (PurpleChat
*)cnode
;
987 if (purple_account_is_connected(chat
->account
))
988 purple_blist_remove_chat(chat
);
994 purple_blist_remove_group(group
);
998 finch_blist_remove_node(PurpleBlistNode
*node
)
1000 if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1001 remove_contact((PurpleContact
*)node
);
1002 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1003 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
1004 PurpleGroup
*group
= purple_buddy_get_group(buddy
);
1005 purple_account_remove_buddy(purple_buddy_get_account(buddy
), buddy
, group
);
1006 purple_blist_remove_buddy(buddy
);
1007 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
1008 purple_blist_remove_chat((PurpleChat
*)node
);
1009 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
1010 remove_group((PurpleGroup
*)node
);
1015 finch_blist_remove_node_cb(PurpleBlistNode
*node
, PurpleBlistNode
*selected
)
1017 PurpleAccount
*account
= NULL
;
1019 const char *name
, *sec
= NULL
;
1021 /* XXX: could be a contact */
1022 if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1023 PurpleContact
*c
= (PurpleContact
*)node
;
1024 name
= purple_contact_get_alias(c
);
1025 if (c
->totalsize
> 1)
1026 sec
= _("Removing this contact will also remove all the buddies in the contact");
1027 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1028 name
= purple_buddy_get_name((PurpleBuddy
*)node
);
1029 account
= purple_buddy_get_account((PurpleBuddy
*)node
);
1030 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
1031 name
= purple_chat_get_name((PurpleChat
*)node
);
1032 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
1033 name
= ((PurpleGroup
*)node
)->name
;
1034 sec
= _("Removing this group will also remove all the buddies in the group");
1039 primary
= g_strdup_printf(_("Are you sure you want to remove %s?"), name
);
1041 /* XXX: anything to do with the returned ui-handle? */
1042 purple_request_action(node
, _("Confirm Remove"),
1045 account
, name
, NULL
,
1047 _("Remove"), finch_blist_remove_node
,
1053 finch_blist_toggle_tag_buddy(PurpleBlistNode
*node
)
1058 if (ggblist
->tagged
&& (iter
= g_list_find(ggblist
->tagged
, node
)) != NULL
) {
1059 ggblist
->tagged
= g_list_delete_link(ggblist
->tagged
, iter
);
1061 ggblist
->tagged
= g_list_prepend(ggblist
->tagged
, node
);
1063 if (PURPLE_BLIST_NODE_IS_CONTACT(node
))
1064 node
= (PurpleBlistNode
*)purple_contact_get_priority_buddy((PurpleContact
*)node
);
1065 if (PURPLE_BLIST_NODE_IS_BUDDY(node
))
1066 update_buddy_display((PurpleBuddy
*)node
, ggblist
);
1068 update_node_display(node
, ggblist
);
1072 finch_blist_place_tagged(PurpleBlistNode
*target
)
1074 PurpleGroup
*tg
= NULL
;
1075 PurpleContact
*tc
= NULL
;
1080 if (PURPLE_BLIST_NODE_IS_GROUP(target
))
1081 tg
= (PurpleGroup
*)target
;
1082 else if (PURPLE_BLIST_NODE_IS_BUDDY(target
)) {
1083 tc
= (PurpleContact
*)target
->parent
;
1084 tg
= (PurpleGroup
*)target
->parent
->parent
;
1086 if (PURPLE_BLIST_NODE_IS_CONTACT(target
))
1087 tc
= (PurpleContact
*)target
;
1088 tg
= (PurpleGroup
*)target
->parent
;
1091 if (ggblist
->tagged
) {
1092 GList
*list
= ggblist
->tagged
;
1093 ggblist
->tagged
= NULL
;
1095 PurpleBlistNode
*node
= list
->data
;
1096 list
= g_list_delete_link(list
, list
);
1098 if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
1099 update_node_display(node
, ggblist
);
1100 /* Add the group after the current group */
1101 purple_blist_add_group((PurpleGroup
*)node
, (PurpleBlistNode
*)tg
);
1102 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1103 update_buddy_display(purple_contact_get_priority_buddy((PurpleContact
*)node
), ggblist
);
1104 if ((PurpleBlistNode
*)tg
== target
) {
1105 /* The target is a group, just add the contact to the group. */
1106 purple_blist_add_contact((PurpleContact
*)node
, tg
, NULL
);
1108 /* The target is either a buddy, or a contact. Merge with that contact. */
1109 purple_blist_merge_contact((PurpleContact
*)node
, (PurpleBlistNode
*)tc
);
1111 /* The target is a chat. Add the contact to the group after this chat. */
1112 purple_blist_add_contact((PurpleContact
*)node
, NULL
, target
);
1114 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1115 update_buddy_display((PurpleBuddy
*)node
, ggblist
);
1116 if ((PurpleBlistNode
*)tg
== target
) {
1117 /* The target is a group. Add this buddy in a new contact under this group. */
1118 purple_blist_add_buddy((PurpleBuddy
*)node
, NULL
, tg
, NULL
);
1119 } else if (PURPLE_BLIST_NODE_IS_CONTACT(target
)) {
1120 /* Add to the contact. */
1121 purple_blist_add_buddy((PurpleBuddy
*)node
, tc
, NULL
, NULL
);
1122 } else if (PURPLE_BLIST_NODE_IS_BUDDY(target
)) {
1123 /* Add to the contact after the selected buddy. */
1124 purple_blist_add_buddy((PurpleBuddy
*)node
, NULL
, NULL
, target
);
1125 } else if (PURPLE_BLIST_NODE_IS_CHAT(target
)) {
1126 /* Add to the selected chat's group. */
1127 purple_blist_add_buddy((PurpleBuddy
*)node
, NULL
, tg
, NULL
);
1129 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
1130 update_node_display(node
, ggblist
);
1131 if ((PurpleBlistNode
*)tg
== target
)
1132 purple_blist_add_chat((PurpleChat
*)node
, tg
, NULL
);
1134 purple_blist_add_chat((PurpleChat
*)node
, NULL
, target
);
1141 context_menu_destroyed(GntWidget
*widget
, FinchBlist
*ggblist
)
1143 ggblist
->context
= NULL
;
1147 draw_context_menu(FinchBlist
*ggblist
)
1149 PurpleBlistNode
*node
= NULL
;
1150 GntWidget
*context
= NULL
;
1151 GntTree
*tree
= NULL
;
1152 int x
, y
, top
, width
;
1155 if (ggblist
->context
)
1158 tree
= GNT_TREE(ggblist
->tree
);
1160 node
= gnt_tree_get_selection_data(tree
);
1162 if (ggblist
->tooltip
)
1163 remove_tooltip(ggblist
);
1165 ggblist
->cnode
= node
;
1167 ggblist
->context
= context
= gnt_menu_new(GNT_MENU_POPUP
);
1168 g_signal_connect(G_OBJECT(context
), "destroy", G_CALLBACK(context_menu_destroyed
), ggblist
);
1171 create_group_menu(GNT_MENU(context
), NULL
);
1172 title
= g_strdup(_("Buddy List"));
1173 } else if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1174 create_buddy_menu(GNT_MENU(context
),
1175 purple_contact_get_priority_buddy((PurpleContact
*)node
));
1176 title
= g_strdup(purple_contact_get_alias((PurpleContact
*)node
));
1177 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1178 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
1179 create_buddy_menu(GNT_MENU(context
), buddy
);
1180 title
= g_strdup(purple_buddy_get_name(buddy
));
1181 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
1182 PurpleChat
*chat
= (PurpleChat
*)node
;
1183 create_chat_menu(GNT_MENU(context
), chat
);
1184 title
= g_strdup(purple_chat_get_name(chat
));
1185 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
1186 PurpleGroup
*group
= (PurpleGroup
*)node
;
1187 create_group_menu(GNT_MENU(context
), group
);
1188 title
= g_strdup(group
->name
);
1191 append_extended_menu(GNT_MENU(context
), node
);
1193 /* These are common for everything */
1195 add_custom_action(GNT_MENU(context
),
1196 PURPLE_BLIST_NODE_IS_GROUP(node
) ? _("Rename") : _("Alias"),
1197 PURPLE_CALLBACK(finch_blist_rename_node_cb
), node
);
1198 add_custom_action(GNT_MENU(context
), _("Remove"),
1199 PURPLE_CALLBACK(finch_blist_remove_node_cb
), node
);
1201 if (ggblist
->tagged
&& (PURPLE_BLIST_NODE_IS_CONTACT(node
)
1202 || PURPLE_BLIST_NODE_IS_GROUP(node
))) {
1203 add_custom_action(GNT_MENU(context
), _("Place tagged"),
1204 PURPLE_CALLBACK(finch_blist_place_tagged
), node
);
1207 if (PURPLE_BLIST_NODE_IS_BUDDY(node
) || PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1208 add_custom_action(GNT_MENU(context
), _("Toggle Tag"),
1209 PURPLE_CALLBACK(finch_blist_toggle_tag_buddy
), node
);
1213 /* Set the position for the popup */
1214 gnt_widget_get_position(GNT_WIDGET(tree
), &x
, &y
);
1215 gnt_widget_get_size(GNT_WIDGET(tree
), &width
, NULL
);
1216 top
= gnt_tree_get_selection_visible_line(tree
);
1221 gnt_widget_set_position(context
, x
, y
);
1222 gnt_screen_menu_show(GNT_MENU(context
));
1227 tooltip_for_buddy(PurpleBuddy
*buddy
, GString
*str
)
1230 PurplePluginProtocolInfo
*prpl_info
;
1231 PurpleAccount
*account
;
1232 PurpleNotifyUserInfo
*user_info
;
1233 const char *alias
= purple_buddy_get_alias(buddy
);
1236 user_info
= purple_notify_user_info_new();
1238 account
= purple_buddy_get_account(buddy
);
1240 if (g_utf8_collate(purple_buddy_get_name(buddy
), alias
))
1241 purple_notify_user_info_add_pair(user_info
, _("Nickname"), alias
);
1243 tmp
= g_strdup_printf("%s (%s)",
1244 purple_account_get_username(account
),
1245 purple_account_get_protocol_name(account
));
1246 purple_notify_user_info_add_pair(user_info
, _("Account"), tmp
);
1249 prpl
= purple_find_prpl(purple_account_get_protocol_id(account
));
1250 prpl_info
= PURPLE_PLUGIN_PROTOCOL_INFO(prpl
);
1251 if (prpl_info
&& prpl_info
->tooltip_text
) {
1252 prpl_info
->tooltip_text(buddy
, user_info
, TRUE
);
1255 if (purple_prefs_get_bool("/finch/blist/idletime")) {
1256 PurplePresence
*pre
= purple_buddy_get_presence(buddy
);
1257 if (purple_presence_is_idle(pre
)) {
1258 time_t idle
= purple_presence_get_idle_time(pre
);
1260 char *st
= purple_str_seconds_to_string(time(NULL
) - idle
);
1261 purple_notify_user_info_add_pair(user_info
, _("Idle"), st
);
1267 tmp
= purple_notify_user_info_get_text_with_newline(user_info
, "<BR>");
1268 purple_notify_user_info_destroy(user_info
);
1270 strip
= purple_markup_strip_html(tmp
);
1271 g_string_append(str
, strip
);
1277 make_sure_text_fits(GString
*string
)
1279 int maxw
= getmaxx(stdscr
) - 3;
1280 char *str
= gnt_util_onscreen_fit_string(string
->str
, maxw
);
1281 string
= g_string_assign(string
, str
);
1287 draw_tooltip_real(FinchBlist
*ggblist
)
1289 PurpleBlistNode
*node
;
1290 int x
, y
, top
, width
, w
, h
;
1293 GntWidget
*widget
, *box
, *tv
;
1297 widget
= ggblist
->tree
;
1298 tree
= GNT_TREE(widget
);
1300 if (!gnt_widget_has_focus(ggblist
->tree
) ||
1301 (ggblist
->context
&& !GNT_WIDGET_IS_FLAG_SET(ggblist
->context
, GNT_WIDGET_INVISIBLE
)))
1304 if (ggblist
->tooltip
)
1306 /* XXX: Once we can properly redraw on expose events, this can be removed at the end
1307 * to avoid the blinking*/
1308 remove_tooltip(ggblist
);
1311 node
= gnt_tree_get_selection_data(tree
);
1315 str
= g_string_new("");
1317 if (PURPLE_BLIST_NODE_IS_CONTACT(node
)) {
1318 PurpleBuddy
*pr
= purple_contact_get_priority_buddy((PurpleContact
*)node
);
1319 gboolean offline
= !PURPLE_BUDDY_IS_ONLINE(pr
);
1320 gboolean showoffline
= purple_prefs_get_bool(PREF_ROOT
"/showoffline");
1321 const char *name
= purple_buddy_get_name(pr
);
1323 title
= g_strdup(name
);
1324 tooltip_for_buddy(pr
, str
);
1325 for (node
= node
->child
; node
; node
= node
->next
) {
1326 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
1328 int value
= purple_blist_node_get_int(node
, "last_seen");
1329 if (value
> lastseen
)
1332 if (node
== (PurpleBlistNode
*)pr
)
1334 if (!purple_account_is_connected(buddy
->account
))
1336 if (!showoffline
&& !PURPLE_BUDDY_IS_ONLINE(buddy
))
1338 str
= g_string_append(str
, "\n----------\n");
1339 tooltip_for_buddy(buddy
, str
);
1341 } else if (PURPLE_BLIST_NODE_IS_BUDDY(node
)) {
1342 PurpleBuddy
*buddy
= (PurpleBuddy
*)node
;
1343 tooltip_for_buddy(buddy
, str
);
1344 title
= g_strdup(purple_buddy_get_name(buddy
));
1345 if (!PURPLE_BUDDY_IS_ONLINE((PurpleBuddy
*)node
))
1346 lastseen
= purple_blist_node_get_int(node
, "last_seen");
1347 } else if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
1348 PurpleGroup
*group
= (PurpleGroup
*)node
;
1350 g_string_append_printf(str
, _("Online: %d\nTotal: %d"),
1351 purple_blist_get_group_online_count(group
),
1352 purple_blist_get_group_size(group
, FALSE
));
1354 title
= g_strdup(group
->name
);
1355 } else if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
1356 PurpleChat
*chat
= (PurpleChat
*)node
;
1357 PurpleAccount
*account
= chat
->account
;
1359 g_string_append_printf(str
, _("Account: %s (%s)"),
1360 purple_account_get_username(account
),
1361 purple_account_get_protocol_name(account
));
1363 title
= g_strdup(purple_chat_get_name(chat
));
1365 g_string_free(str
, TRUE
);
1370 char *tmp
= purple_str_seconds_to_string(time(NULL
) - lastseen
);
1371 g_string_append_printf(str
, _("\nLast Seen: %s ago"), tmp
);
1375 gnt_widget_get_position(widget
, &x
, &y
);
1376 gnt_widget_get_size(widget
, &width
, NULL
);
1377 top
= gnt_tree_get_selection_visible_line(tree
);
1382 box
= gnt_box_new(FALSE
, FALSE
);
1383 gnt_box_set_toplevel(GNT_BOX(box
), TRUE
);
1384 GNT_WIDGET_SET_FLAGS(box
, GNT_WIDGET_NO_SHADOW
);
1385 gnt_box_set_title(GNT_BOX(box
), title
);
1387 str
= make_sure_text_fits(str
);
1388 gnt_util_get_text_bound(str
->str
, &w
, &h
);
1390 tv
= gnt_text_view_new();
1391 gnt_widget_set_size(tv
, w
+ 1, h
);
1392 gnt_box_add_widget(GNT_BOX(box
), tv
);
1394 gnt_widget_set_position(box
, x
, y
);
1395 GNT_WIDGET_UNSET_FLAGS(box
, GNT_WIDGET_CAN_TAKE_FOCUS
);
1396 GNT_WIDGET_SET_FLAGS(box
, GNT_WIDGET_TRANSIENT
);
1397 gnt_widget_draw(box
);
1399 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(tv
), str
->str
, GNT_TEXT_FLAG_NORMAL
);
1400 gnt_text_view_scroll(GNT_TEXT_VIEW(tv
), 0);
1403 g_string_free(str
, TRUE
);
1404 ggblist
->tooltip
= box
;
1405 ggblist
->tnode
= node
;
1407 gnt_widget_set_name(ggblist
->tooltip
, "tooltip");
1412 draw_tooltip(FinchBlist
*ggblist
)
1414 /* When an account has signed off, it removes one buddy at a time.
1415 * Drawing the tooltip after removing each buddy is expensive. On
1416 * top of that, if the selected buddy belongs to the disconnected
1417 * account, then retreiving the tooltip for that causes crash. So
1418 * let's make sure we wait for all the buddies to be removed first.*/
1419 int id
= g_timeout_add(0, (GSourceFunc
)draw_tooltip_real
, ggblist
);
1420 g_object_set_data_full(G_OBJECT(ggblist
->window
), "draw_tooltip_calback",
1421 GINT_TO_POINTER(id
), (GDestroyNotify
)g_source_remove
);
1425 selection_changed(GntWidget
*widget
, gpointer old
, gpointer current
, FinchBlist
*ggblist
)
1427 remove_peripherals(ggblist
);
1428 draw_tooltip(ggblist
);
1432 context_menu(GntWidget
*widget
, FinchBlist
*ggblist
)
1434 draw_context_menu(ggblist
);
1439 key_pressed(GntWidget
*widget
, const char *text
, FinchBlist
*ggblist
)
1441 if (text
[0] == 27 && text
[1] == 0) {
1442 /* Escape was pressed */
1443 remove_peripherals(ggblist
);
1444 } else if (strcmp(text
, GNT_KEY_CTRL_O
) == 0) {
1445 purple_prefs_set_bool(PREF_ROOT
"/showoffline",
1446 !purple_prefs_get_bool(PREF_ROOT
"/showoffline"));
1447 } else if (GNT_TREE(ggblist
->tree
)->search
== NULL
) {
1448 if (strcmp(text
, "t") == 0) {
1449 finch_blist_toggle_tag_buddy(gnt_tree_get_selection_data(GNT_TREE(ggblist
->tree
)));
1450 gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist
->tree
), "move-down");
1451 } else if (strcmp(text
, "a") == 0) {
1452 finch_blist_place_tagged(gnt_tree_get_selection_data(GNT_TREE(ggblist
->tree
)));
1462 update_node_display(PurpleBlistNode
*node
, FinchBlist
*ggblist
)
1464 GntTextFormatFlags flag
= 0;
1465 if (ggblist
->tagged
&& g_list_find(ggblist
->tagged
, node
))
1466 flag
|= GNT_TEXT_FLAG_BOLD
;
1467 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), node
, flag
);
1471 update_buddy_display(PurpleBuddy
*buddy
, FinchBlist
*ggblist
)
1473 PurpleContact
*contact
;
1474 GntTextFormatFlags bflag
= 0, cflag
= 0;
1476 contact
= purple_buddy_get_contact(buddy
);
1478 gnt_tree_change_text(GNT_TREE(ggblist
->tree
), buddy
, 0, get_display_name((PurpleBlistNode
*)buddy
));
1479 gnt_tree_change_text(GNT_TREE(ggblist
->tree
), contact
, 0, get_display_name((PurpleBlistNode
*)contact
));
1481 if (ggblist
->tagged
&& g_list_find(ggblist
->tagged
, buddy
))
1482 bflag
|= GNT_TEXT_FLAG_BOLD
;
1483 if (ggblist
->tagged
&& g_list_find(ggblist
->tagged
, contact
))
1484 cflag
|= GNT_TEXT_FLAG_BOLD
;
1486 if (ggblist
->tnode
== (PurpleBlistNode
*)buddy
)
1487 draw_tooltip(ggblist
);
1489 if (purple_presence_is_idle(purple_buddy_get_presence(buddy
))) {
1490 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), buddy
, bflag
| GNT_TEXT_FLAG_DIM
);
1491 if (buddy
== purple_contact_get_priority_buddy(contact
))
1492 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), contact
, cflag
| GNT_TEXT_FLAG_DIM
);
1494 update_buddy_display(purple_contact_get_priority_buddy(contact
), ggblist
);
1496 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), buddy
, bflag
);
1497 if (buddy
== purple_contact_get_priority_buddy(contact
))
1498 gnt_tree_set_row_flags(GNT_TREE(ggblist
->tree
), contact
, cflag
);
1500 update_buddy_display(purple_contact_get_priority_buddy(contact
), ggblist
);
1505 buddy_status_changed(PurpleBuddy
*buddy
, PurpleStatus
*old
, PurpleStatus
*now
, FinchBlist
*ggblist
)
1507 update_buddy_display(buddy
, ggblist
);
1511 buddy_idle_changed(PurpleBuddy
*buddy
, int old
, int new, FinchBlist
*ggblist
)
1513 update_buddy_display(buddy
, ggblist
);
1517 remove_peripherals(FinchBlist
*ggblist
)
1519 if (ggblist
->tooltip
)
1520 remove_tooltip(ggblist
);
1521 else if (ggblist
->context
)
1522 gnt_widget_destroy(ggblist
->context
);
1526 size_changed_cb(GntWidget
*w
, int wi
, int h
)
1529 gnt_widget_get_size(w
, &width
, &height
);
1530 purple_prefs_set_int(PREF_ROOT
"/size/width", width
);
1531 purple_prefs_set_int(PREF_ROOT
"/size/height", height
);
1535 save_position_cb(GntWidget
*w
, int x
, int y
)
1537 purple_prefs_set_int(PREF_ROOT
"/position/x", x
);
1538 purple_prefs_set_int(PREF_ROOT
"/position/y", y
);
1542 reset_blist_window(GntWidget
*window
, gpointer null
)
1544 PurpleBlistNode
*node
;
1545 purple_signals_disconnect_by_handle(finch_blist_get_handle());
1546 purple_get_blist()->ui_data
= NULL
;
1548 node
= purple_blist_get_root();
1550 node
->ui_data
= NULL
;
1551 node
= purple_blist_node_next(node
, TRUE
);
1554 if (ggblist
->typing
)
1555 g_source_remove(ggblist
->typing
);
1556 remove_peripherals(ggblist
);
1557 if (ggblist
->tagged
)
1558 g_list_free(ggblist
->tagged
);
1564 populate_buddylist()
1566 PurpleBlistNode
*node
;
1567 PurpleBuddyList
*list
;
1569 if (strcmp(purple_prefs_get_string(PREF_ROOT
"/sort_type"), "text") == 0) {
1570 gnt_tree_set_compare_func(GNT_TREE(ggblist
->tree
),
1571 (GCompareFunc
)blist_node_compare_text
);
1572 } else if (strcmp(purple_prefs_get_string(PREF_ROOT
"/sort_type"), "status") == 0) {
1573 gnt_tree_set_compare_func(GNT_TREE(ggblist
->tree
),
1574 (GCompareFunc
)blist_node_compare_status
);
1575 } else if (strcmp(purple_prefs_get_string(PREF_ROOT
"/sort_type"), "log") == 0) {
1576 gnt_tree_set_compare_func(GNT_TREE(ggblist
->tree
),
1577 (GCompareFunc
)blist_node_compare_log
);
1580 list
= purple_get_blist();
1581 node
= purple_blist_get_root();
1584 node_update(list
, node
);
1585 node
= purple_blist_node_next(node
, FALSE
);
1590 destroy_status_list(GList
*list
)
1592 g_list_foreach(list
, (GFunc
)g_free
, NULL
);
1597 populate_status_dropdown()
1601 GList
*items
= NULL
;
1602 StatusBoxItem
*item
= NULL
;
1604 /* First the primitives */
1605 PurpleStatusPrimitive prims
[] = {PURPLE_STATUS_AVAILABLE
, PURPLE_STATUS_AWAY
,
1606 PURPLE_STATUS_INVISIBLE
, PURPLE_STATUS_OFFLINE
, PURPLE_STATUS_UNSET
};
1608 gnt_combo_box_remove_all(GNT_COMBO_BOX(ggblist
->status
));
1610 for (i
= 0; prims
[i
] != PURPLE_STATUS_UNSET
; i
++)
1612 item
= g_new0(StatusBoxItem
, 1);
1613 item
->type
= STATUS_PRIMITIVE
;
1614 item
->u
.prim
= prims
[i
];
1615 items
= g_list_prepend(items
, item
);
1616 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
1617 purple_primitive_get_name_from_type(prims
[i
]));
1620 /* Now the popular statuses */
1621 for (iter
= purple_savedstatuses_get_popular(6); iter
; iter
= iter
->next
)
1623 item
= g_new0(StatusBoxItem
, 1);
1624 item
->type
= STATUS_SAVED_POPULAR
;
1625 item
->u
.saved
= iter
->data
;
1626 items
= g_list_prepend(items
, item
);
1627 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
1628 purple_savedstatus_get_title(iter
->data
));
1631 /* New savedstatus */
1632 item
= g_new0(StatusBoxItem
, 1);
1633 item
->type
= STATUS_SAVED_NEW
;
1634 items
= g_list_prepend(items
, item
);
1635 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
1638 /* More savedstatuses */
1639 item
= g_new0(StatusBoxItem
, 1);
1640 item
->type
= STATUS_SAVED_ALL
;
1641 items
= g_list_prepend(items
, item
);
1642 gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist
->status
), item
,
1645 /* The keys for the combobox are created here, and never used
1646 * anywhere else. So make sure the keys are freed when the widget
1648 g_object_set_data_full(G_OBJECT(ggblist
->status
), "list of statuses",
1649 items
, (GDestroyNotify
)destroy_status_list
);
1653 redraw_blist(const char *name
, PurplePrefType type
, gconstpointer val
, gpointer data
)
1655 PurpleBlistNode
*node
, *sel
;
1656 if (ggblist
== NULL
|| ggblist
->window
== NULL
)
1659 sel
= gnt_tree_get_selection_data(GNT_TREE(ggblist
->tree
));
1660 gnt_tree_remove_all(GNT_TREE(ggblist
->tree
));
1661 node
= purple_blist_get_root();
1662 for (; node
; node
= purple_blist_node_next(node
, TRUE
))
1663 node
->ui_data
= NULL
;
1664 populate_buddylist();
1665 gnt_tree_set_selected(GNT_TREE(ggblist
->tree
), sel
);
1666 draw_tooltip(ggblist
);
1669 void finch_blist_init()
1671 purple_prefs_add_none(PREF_ROOT
);
1672 purple_prefs_add_none(PREF_ROOT
"/size");
1673 purple_prefs_add_int(PREF_ROOT
"/size/width", 20);
1674 purple_prefs_add_int(PREF_ROOT
"/size/height", 17);
1675 purple_prefs_add_none(PREF_ROOT
"/position");
1676 purple_prefs_add_int(PREF_ROOT
"/position/x", 0);
1677 purple_prefs_add_int(PREF_ROOT
"/position/y", 0);
1678 purple_prefs_add_bool(PREF_ROOT
"/idletime", TRUE
);
1679 purple_prefs_add_bool(PREF_ROOT
"/showoffline", FALSE
);
1680 purple_prefs_add_string(PREF_ROOT
"/sort_type", "text");
1682 purple_prefs_connect_callback(finch_blist_get_handle(),
1683 PREF_ROOT
"/showoffline", redraw_blist
, NULL
);
1684 purple_prefs_connect_callback(finch_blist_get_handle(),
1685 PREF_ROOT
"/sort_type", redraw_blist
, NULL
);
1687 purple_signal_connect(purple_connections_get_handle(), "signed-on", purple_blist_get_handle(),
1688 G_CALLBACK(account_signed_on_cb
), NULL
);
1693 remove_typing_cb(gpointer null
)
1695 PurpleSavedStatus
*current
;
1696 const char *message
, *newmessage
;
1697 PurpleStatusPrimitive prim
, newprim
;
1698 StatusBoxItem
*item
;
1700 current
= purple_savedstatus_get_current();
1701 message
= purple_savedstatus_get_message(current
);
1702 prim
= purple_savedstatus_get_type(current
);
1704 newmessage
= gnt_entry_get_text(GNT_ENTRY(ggblist
->statustext
));
1705 item
= gnt_combo_box_get_selected_data(GNT_COMBO_BOX(ggblist
->status
));
1707 switch (item
->type
) {
1708 case STATUS_PRIMITIVE
:
1709 newprim
= item
->u
.prim
;
1711 case STATUS_SAVED_POPULAR
:
1712 newprim
= purple_savedstatus_get_type(item
->u
.saved
);
1715 goto end
; /* 'New' or 'Saved' is selected, but this should never happen. */
1718 if (newprim
!= prim
|| ((message
&& !newmessage
) ||
1719 (!message
&& newmessage
) ||
1720 (message
&& newmessage
&& g_utf8_collate(message
, newmessage
) != 0)))
1722 PurpleSavedStatus
*status
= purple_savedstatus_find_transient_by_type_and_message(newprim
, newmessage
);
1723 /* Holy Crap! That's a LAWNG function name */
1726 status
= purple_savedstatus_new(NULL
, newprim
);
1727 purple_savedstatus_set_message(status
, newmessage
);
1730 purple_savedstatus_activate(status
);
1733 gnt_box_give_focus_to_child(GNT_BOX(ggblist
->window
), ggblist
->tree
);
1735 if (ggblist
->typing
)
1736 g_source_remove(ggblist
->typing
);
1737 ggblist
->typing
= 0;
1742 status_selection_changed(GntComboBox
*box
, StatusBoxItem
*old
, StatusBoxItem
*now
, gpointer null
)
1744 gnt_entry_set_text(GNT_ENTRY(ggblist
->statustext
), NULL
);
1745 if (now
->type
== STATUS_SAVED_POPULAR
)
1747 /* Set the status immediately */
1748 purple_savedstatus_activate(now
->u
.saved
);
1750 else if (now
->type
== STATUS_PRIMITIVE
)
1752 /* Move the focus to the entry box */
1753 /* XXX: Make sure the selected status can have a message */
1754 gnt_box_move_focus(GNT_BOX(ggblist
->window
), 1);
1755 ggblist
->typing
= g_timeout_add(TYPING_TIMEOUT
, (GSourceFunc
)remove_typing_cb
, NULL
);
1757 else if (now
->type
== STATUS_SAVED_ALL
)
1759 /* Restore the selection to reflect current status. */
1760 savedstatus_changed(purple_savedstatus_get_current(), NULL
);
1761 gnt_box_give_focus_to_child(GNT_BOX(ggblist
->window
), ggblist
->tree
);
1762 finch_savedstatus_show_all();
1764 else if (now
->type
== STATUS_SAVED_NEW
)
1766 savedstatus_changed(purple_savedstatus_get_current(), NULL
);
1767 gnt_box_give_focus_to_child(GNT_BOX(ggblist
->window
), ggblist
->tree
);
1768 finch_savedstatus_edit(NULL
);
1771 g_return_if_reached();
1775 status_text_changed(GntEntry
*entry
, const char *text
, gpointer null
)
1777 if ((text
[0] == 27 || (text
[0] == '\t' && text
[1] == '\0')) && ggblist
->typing
== 0)
1780 if (ggblist
->typing
)
1781 g_source_remove(ggblist
->typing
);
1782 ggblist
->typing
= 0;
1784 if (text
[0] == '\r' && text
[1] == 0)
1786 /* Set the status only after you press 'Enter' */
1787 remove_typing_cb(NULL
);
1791 ggblist
->typing
= g_timeout_add(TYPING_TIMEOUT
, (GSourceFunc
)remove_typing_cb
, NULL
);
1796 savedstatus_changed(PurpleSavedStatus
*now
, PurpleSavedStatus
*old
)
1799 PurpleStatusPrimitive prim
;
1800 const char *message
;
1801 gboolean found
= FALSE
, saved
= TRUE
;
1806 /* Block the signals we don't want to emit */
1807 g_signal_handlers_block_matched(ggblist
->status
, G_SIGNAL_MATCH_FUNC
,
1808 0, 0, NULL
, status_selection_changed
, NULL
);
1809 g_signal_handlers_block_matched(ggblist
->statustext
, G_SIGNAL_MATCH_FUNC
,
1810 0, 0, NULL
, status_text_changed
, NULL
);
1812 prim
= purple_savedstatus_get_type(now
);
1813 message
= purple_savedstatus_get_message(now
);
1815 /* Rebuild the status dropdown */
1816 populate_status_dropdown();
1819 list
= g_object_get_data(G_OBJECT(ggblist
->status
), "list of statuses");
1820 for (; list
; list
= list
->next
)
1822 StatusBoxItem
*item
= list
->data
;
1823 if ((saved
&& item
->type
!= STATUS_PRIMITIVE
&& item
->u
.saved
== now
) ||
1824 (!saved
&& item
->type
== STATUS_PRIMITIVE
&& item
->u
.prim
== prim
))
1826 char *mess
= purple_unescape_html(message
);
1827 gnt_combo_box_set_selected(GNT_COMBO_BOX(ggblist
->status
), item
);
1828 gnt_entry_set_text(GNT_ENTRY(ggblist
->statustext
), mess
);
1829 gnt_widget_draw(ggblist
->status
);
1840 g_signal_handlers_unblock_matched(ggblist
->status
, G_SIGNAL_MATCH_FUNC
,
1841 0, 0, NULL
, status_selection_changed
, NULL
);
1842 g_signal_handlers_unblock_matched(ggblist
->statustext
, G_SIGNAL_MATCH_FUNC
,
1843 0, 0, NULL
, status_text_changed
, NULL
);
1847 blist_node_compare_position(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
1849 while ((n1
= n1
->prev
) != NULL
)
1856 blist_node_compare_text(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
1858 const char *s1
, *s2
;
1862 g_return_val_if_fail(n1
->type
== n2
->type
, -1);
1866 case PURPLE_BLIST_CHAT_NODE
:
1867 s1
= purple_chat_get_name((PurpleChat
*)n1
);
1868 s2
= purple_chat_get_name((PurpleChat
*)n2
);
1870 case PURPLE_BLIST_BUDDY_NODE
:
1871 return purple_presence_compare(purple_buddy_get_presence((PurpleBuddy
*)n1
),
1872 purple_buddy_get_presence((PurpleBuddy
*)n2
));
1874 case PURPLE_BLIST_CONTACT_NODE
:
1875 s1
= purple_contact_get_alias((PurpleContact
*)n1
);
1876 s2
= purple_contact_get_alias((PurpleContact
*)n2
);
1879 return blist_node_compare_position(n1
, n2
);
1882 us1
= g_utf8_strup(s1
, -1);
1883 us2
= g_utf8_strup(s2
, -1);
1884 ret
= g_utf8_collate(us1
, us2
);
1892 blist_node_compare_status(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
1896 g_return_val_if_fail(n1
->type
== n2
->type
, -1);
1899 case PURPLE_BLIST_CONTACT_NODE
:
1900 n1
= (PurpleBlistNode
*)purple_contact_get_priority_buddy((PurpleContact
*)n1
);
1901 n2
= (PurpleBlistNode
*)purple_contact_get_priority_buddy((PurpleContact
*)n2
);
1902 /* now compare the presence of the priority buddies */
1903 case PURPLE_BLIST_BUDDY_NODE
:
1904 ret
= purple_presence_compare(purple_buddy_get_presence((PurpleBuddy
*)n1
),
1905 purple_buddy_get_presence((PurpleBuddy
*)n2
));
1910 return blist_node_compare_position(n1
, n2
);
1914 /* Sort alphabetically if presence is not comparable */
1915 ret
= blist_node_compare_text(n1
, n2
);
1921 get_contact_log_size(PurpleBlistNode
*c
)
1924 PurpleBlistNode
*node
;
1926 for (node
= c
->child
; node
; node
= node
->next
) {
1927 PurpleBuddy
*b
= (PurpleBuddy
*)node
;
1928 log
+= purple_log_get_total_size(PURPLE_LOG_IM
, b
->name
, b
->account
);
1935 blist_node_compare_log(PurpleBlistNode
*n1
, PurpleBlistNode
*n2
)
1938 PurpleBuddy
*b1
, *b2
;
1940 g_return_val_if_fail(n1
->type
== n2
->type
, -1);
1943 case PURPLE_BLIST_BUDDY_NODE
:
1944 b1
= (PurpleBuddy
*)n1
;
1945 b2
= (PurpleBuddy
*)n2
;
1946 ret
= purple_log_get_total_size(PURPLE_LOG_IM
, b2
->name
, b2
->account
) -
1947 purple_log_get_total_size(PURPLE_LOG_IM
, b1
->name
, b1
->account
);
1951 case PURPLE_BLIST_CONTACT_NODE
:
1952 ret
= get_contact_log_size(n2
) - get_contact_log_size(n1
);
1957 return blist_node_compare_position(n1
, n2
);
1959 ret
= blist_node_compare_text(n1
, n2
);
1964 blist_clicked(GntTree
*tree
, GntMouseEvent event
, int x
, int y
, gpointer ggblist
)
1966 if (event
== GNT_RIGHT_MOUSE_DOWN
) {
1967 draw_context_menu(ggblist
);
1973 plugin_action(GntMenuItem
*item
, gpointer data
)
1975 PurplePluginAction
*action
= data
;
1976 if (action
&& action
->callback
)
1977 action
->callback(action
);
1981 build_plugin_actions(GntMenuItem
*item
, PurplePlugin
*plugin
, gpointer context
)
1983 GntWidget
*sub
= gnt_menu_new(GNT_MENU_POPUP
);
1985 GntMenuItem
*menuitem
;
1987 gnt_menuitem_set_submenu(item
, GNT_MENU(sub
));
1988 for (actions
= PURPLE_PLUGIN_ACTIONS(plugin
, context
); actions
;
1989 actions
= g_list_delete_link(actions
, actions
)) {
1990 if (actions
->data
) {
1991 PurplePluginAction
*action
= actions
->data
;
1992 action
->plugin
= plugin
;
1993 action
->context
= context
;
1994 menuitem
= gnt_menuitem_new(action
->label
);
1995 gnt_menu_add_item(GNT_MENU(sub
), menuitem
);
1997 gnt_menuitem_set_callback(menuitem
, plugin_action
, action
);
1998 g_object_set_data_full(G_OBJECT(menuitem
), "plugin_action",
1999 action
, (GDestroyNotify
)purple_plugin_action_free
);
2005 reconstruct_plugins_menu()
2014 if (ggblist
->plugins
== NULL
)
2015 ggblist
->plugins
= gnt_menuitem_new(_("Plugins"));
2017 plg
= ggblist
->plugins
;
2018 sub
= gnt_menu_new(GNT_MENU_POPUP
);
2019 gnt_menuitem_set_submenu(plg
, GNT_MENU(sub
));
2021 for (iter
= purple_plugins_get_loaded(); iter
; iter
= iter
->next
) {
2022 PurplePlugin
*plugin
= iter
->data
;
2024 if (PURPLE_IS_PROTOCOL_PLUGIN(plugin
))
2027 if (!PURPLE_PLUGIN_HAS_ACTIONS(plugin
))
2030 item
= gnt_menuitem_new(_(plugin
->info
->name
));
2031 gnt_menu_add_item(GNT_MENU(sub
), item
);
2032 build_plugin_actions(item
, plugin
, NULL
);
2037 reconstruct_accounts_menu()
2040 GntMenuItem
*acc
, *item
;
2046 if (ggblist
->accounts
== NULL
)
2047 ggblist
->accounts
= gnt_menuitem_new(_("Accounts"));
2049 acc
= ggblist
->accounts
;
2050 sub
= gnt_menu_new(GNT_MENU_POPUP
);
2051 gnt_menuitem_set_submenu(acc
, GNT_MENU(sub
));
2053 for (iter
= purple_accounts_get_all_active(); iter
;
2054 iter
= g_list_delete_link(iter
, iter
)) {
2055 PurpleAccount
*account
= iter
->data
;
2056 PurpleConnection
*gc
= purple_account_get_connection(account
);
2059 if (!gc
|| !PURPLE_CONNECTION_IS_CONNECTED(gc
))
2063 if (PURPLE_PLUGIN_HAS_ACTIONS(prpl
)) {
2064 item
= gnt_menuitem_new(purple_account_get_username(account
));
2065 gnt_menu_add_item(GNT_MENU(sub
), item
);
2066 build_plugin_actions(item
, prpl
, gc
);
2072 account_signed_on_cb(PurpleConnection
*pc
, gpointer null
)
2074 PurpleBlistNode
*node
;
2076 for (node
= purple_blist_get_root(); node
;
2077 node
= purple_blist_node_next(node
, FALSE
)) {
2078 if (PURPLE_BLIST_NODE_IS_CHAT(node
)) {
2079 PurpleChat
*chat
= (PurpleChat
*)node
;
2080 if (chat
->account
== purple_connection_get_account(pc
) &&
2081 purple_blist_node_get_bool(node
, "gnt-autojoin"))
2082 serv_join_chat(purple_account_get_connection(chat
->account
), chat
->components
);
2087 static void show_offline_cb(GntMenuItem
*item
, gpointer n
)
2089 purple_prefs_set_bool(PREF_ROOT
"/showoffline",
2090 !purple_prefs_get_bool(PREF_ROOT
"/showoffline"));
2093 static void sort_blist_change_cb(GntMenuItem
*item
, gpointer n
)
2095 purple_prefs_set_string(PREF_ROOT
"/sort_type", n
);
2098 /* XXX: send_im_select* -- Xerox */
2100 send_im_select_cb(gpointer data
, PurpleRequestFields
*fields
)
2102 PurpleAccount
*account
;
2103 const char *username
;
2105 account
= purple_request_fields_get_account(fields
, "account");
2106 username
= purple_request_fields_get_string(fields
, "screenname");
2108 purple_conversation_new(PURPLE_CONV_TYPE_IM
, account
, username
);
2112 send_im_select(GntMenuItem
*item
, gpointer n
)
2114 PurpleRequestFields
*fields
;
2115 PurpleRequestFieldGroup
*group
;
2116 PurpleRequestField
*field
;
2118 fields
= purple_request_fields_new();
2120 group
= purple_request_field_group_new(NULL
);
2121 purple_request_fields_add_group(fields
, group
);
2123 field
= purple_request_field_string_new("screenname", _("_Name"), NULL
, FALSE
);
2124 purple_request_field_set_type_hint(field
, "screenname");
2125 purple_request_field_set_required(field
, TRUE
);
2126 purple_request_field_group_add_field(group
, field
);
2128 field
= purple_request_field_account_new("account", _("_Account"), NULL
);
2129 purple_request_field_set_type_hint(field
, "account");
2130 purple_request_field_set_visible(field
,
2131 (purple_connections_get_all() != NULL
&&
2132 purple_connections_get_all()->next
!= NULL
));
2133 purple_request_field_set_required(field
, TRUE
);
2134 purple_request_field_group_add_field(group
, field
);
2136 purple_request_fields(purple_get_blist(), _("New Instant Message"),
2138 _("Please enter the screen name or alias of the person "
2139 "you would like to IM."),
2141 _("OK"), G_CALLBACK(send_im_select_cb
),
2150 GntWidget
*menu
, *sub
;
2157 window
= GNT_WINDOW(ggblist
->window
);
2158 ggblist
->menu
= menu
= gnt_menu_new(GNT_MENU_TOPLEVEL
);
2159 gnt_window_set_menu(window
, GNT_MENU(menu
));
2161 item
= gnt_menuitem_new(_("Options"));
2162 gnt_menu_add_item(GNT_MENU(menu
), item
);
2164 sub
= gnt_menu_new(GNT_MENU_POPUP
);
2165 gnt_menuitem_set_submenu(item
, GNT_MENU(sub
));
2167 item
= gnt_menuitem_new(_("Send IM..."));
2168 gnt_menu_add_item(GNT_MENU(sub
), item
);
2169 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), send_im_select
, NULL
);
2171 item
= gnt_menuitem_check_new(_("Toggle offline buddies"));
2172 gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item
),
2173 purple_prefs_get_bool(PREF_ROOT
"/showoffline"));
2174 gnt_menu_add_item(GNT_MENU(sub
), item
);
2175 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), show_offline_cb
, NULL
);
2177 item
= gnt_menuitem_new(_("Sort by status"));
2178 gnt_menu_add_item(GNT_MENU(sub
), item
);
2179 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), sort_blist_change_cb
, "status");
2181 item
= gnt_menuitem_new(_("Sort alphabetically"));
2182 gnt_menu_add_item(GNT_MENU(sub
), item
);
2183 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), sort_blist_change_cb
, "text");
2185 item
= gnt_menuitem_new(_("Sort by log size"));
2186 gnt_menu_add_item(GNT_MENU(sub
), item
);
2187 gnt_menuitem_set_callback(GNT_MENU_ITEM(item
), sort_blist_change_cb
, "log");
2189 reconstruct_accounts_menu();
2190 gnt_menu_add_item(GNT_MENU(menu
), ggblist
->accounts
);
2192 reconstruct_plugins_menu();
2193 gnt_menu_add_item(GNT_MENU(menu
), ggblist
->plugins
);
2196 void finch_blist_show()
2198 blist_show(purple_get_blist());
2202 group_collapsed(GntWidget
*widget
, PurpleBlistNode
*node
, gboolean collapsed
, gpointer null
)
2204 if (PURPLE_BLIST_NODE_IS_GROUP(node
))
2205 purple_blist_node_set_bool(node
, "collapsed", collapsed
);
2209 blist_show(PurpleBuddyList
*list
)
2211 if (ggblist
== NULL
)
2213 else if (ggblist
->window
)
2216 ggblist
->window
= gnt_vwindow_new(FALSE
);
2217 gnt_widget_set_name(ggblist
->window
, "buddylist");
2218 gnt_box_set_toplevel(GNT_BOX(ggblist
->window
), TRUE
);
2219 gnt_box_set_title(GNT_BOX(ggblist
->window
), _("Buddy List"));
2220 gnt_box_set_pad(GNT_BOX(ggblist
->window
), 0);
2222 ggblist
->tree
= gnt_tree_new();
2224 GNT_WIDGET_SET_FLAGS(ggblist
->tree
, GNT_WIDGET_NO_BORDER
);
2225 gnt_widget_set_size(ggblist
->tree
, purple_prefs_get_int(PREF_ROOT
"/size/width"),
2226 purple_prefs_get_int(PREF_ROOT
"/size/height"));
2227 gnt_widget_set_position(ggblist
->window
, purple_prefs_get_int(PREF_ROOT
"/position/x"),
2228 purple_prefs_get_int(PREF_ROOT
"/position/y"));
2230 gnt_tree_set_col_width(GNT_TREE(ggblist
->tree
), 0,
2231 purple_prefs_get_int(PREF_ROOT
"/size/width") - 1);
2233 gnt_box_add_widget(GNT_BOX(ggblist
->window
), ggblist
->tree
);
2235 ggblist
->status
= gnt_combo_box_new();
2236 gnt_box_add_widget(GNT_BOX(ggblist
->window
), ggblist
->status
);
2237 ggblist
->statustext
= gnt_entry_new(NULL
);
2238 gnt_box_add_widget(GNT_BOX(ggblist
->window
), ggblist
->statustext
);
2240 gnt_widget_show(ggblist
->window
);
2242 purple_signal_connect(purple_connections_get_handle(), "signed-on", finch_blist_get_handle(),
2243 PURPLE_CALLBACK(reconstruct_accounts_menu
), NULL
);
2244 purple_signal_connect(purple_connections_get_handle(), "signed-off", finch_blist_get_handle(),
2245 PURPLE_CALLBACK(reconstruct_accounts_menu
), NULL
);
2246 purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", finch_blist_get_handle(),
2247 PURPLE_CALLBACK(buddy_status_changed
), ggblist
);
2248 purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", finch_blist_get_handle(),
2249 PURPLE_CALLBACK(buddy_idle_changed
), ggblist
);
2251 purple_signal_connect(purple_plugins_get_handle(), "plugin-load", finch_blist_get_handle(),
2252 PURPLE_CALLBACK(reconstruct_plugins_menu
), NULL
);
2253 purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", finch_blist_get_handle(),
2254 PURPLE_CALLBACK(reconstruct_plugins_menu
), NULL
);
2257 purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_blist_get_handle(),
2258 PURPLE_CALLBACK(buddy_signed_on
), ggblist
);
2259 purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_blist_get_handle(),
2260 PURPLE_CALLBACK(buddy_signed_off
), ggblist
);
2262 /* These I plan to use to indicate unread-messages etc. */
2263 purple_signal_connect(purple_conversations_get_handle(), "received-im-msg", finch_blist_get_handle(),
2264 PURPLE_CALLBACK(received_im_msg
), list
);
2265 purple_signal_connect(purple_conversations_get_handle(), "sent-im-msg", finch_blist_get_handle(),
2266 PURPLE_CALLBACK(sent_im_msg
), NULL
);
2268 purple_signal_connect(purple_conversations_get_handle(), "received-chat-msg", finch_blist_get_handle(),
2269 PURPLE_CALLBACK(received_chat_msg
), list
);
2272 g_signal_connect(G_OBJECT(ggblist
->tree
), "selection_changed", G_CALLBACK(selection_changed
), ggblist
);
2273 g_signal_connect(G_OBJECT(ggblist
->tree
), "key_pressed", G_CALLBACK(key_pressed
), ggblist
);
2274 g_signal_connect(G_OBJECT(ggblist
->tree
), "context-menu", G_CALLBACK(context_menu
), ggblist
);
2275 g_signal_connect(G_OBJECT(ggblist
->tree
), "collapse-toggled", G_CALLBACK(group_collapsed
), NULL
);
2276 g_signal_connect_after(G_OBJECT(ggblist
->tree
), "clicked", G_CALLBACK(blist_clicked
), ggblist
);
2277 g_signal_connect(G_OBJECT(ggblist
->tree
), "activate", G_CALLBACK(selection_activate
), ggblist
);
2278 g_signal_connect_data(G_OBJECT(ggblist
->tree
), "gained-focus", G_CALLBACK(draw_tooltip
),
2279 ggblist
, 0, G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
2280 g_signal_connect_data(G_OBJECT(ggblist
->tree
), "lost-focus", G_CALLBACK(remove_peripherals
),
2281 ggblist
, 0, G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
2282 g_signal_connect(G_OBJECT(ggblist
->tree
), "size_changed", G_CALLBACK(size_changed_cb
), NULL
);
2283 g_signal_connect(G_OBJECT(ggblist
->window
), "position_set", G_CALLBACK(save_position_cb
), NULL
);
2284 g_signal_connect(G_OBJECT(ggblist
->window
), "destroy", G_CALLBACK(reset_blist_window
), NULL
);
2286 /* Status signals */
2287 purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-changed", finch_blist_get_handle(),
2288 PURPLE_CALLBACK(savedstatus_changed
), NULL
);
2289 g_signal_connect(G_OBJECT(ggblist
->status
), "selection_changed",
2290 G_CALLBACK(status_selection_changed
), NULL
);
2291 g_signal_connect(G_OBJECT(ggblist
->statustext
), "key_pressed",
2292 G_CALLBACK(status_text_changed
), NULL
);
2296 populate_buddylist();
2298 savedstatus_changed(purple_savedstatus_get_current(), NULL
);
2301 void finch_blist_uninit()
2303 if (ggblist
== NULL
)
2306 gnt_widget_destroy(ggblist
->window
);
2311 gboolean
finch_blist_get_position(int *x
, int *y
)
2313 if (!ggblist
|| !ggblist
->window
)
2315 gnt_widget_get_position(ggblist
->window
, x
, y
);
2319 void finch_blist_set_position(int x
, int y
)
2321 gnt_widget_set_position(ggblist
->window
, x
, y
);
2324 gboolean
finch_blist_get_size(int *width
, int *height
)
2326 if (!ggblist
|| !ggblist
->window
)
2328 gnt_widget_get_size(ggblist
->window
, width
, height
);
2332 void finch_blist_set_size(int width
, int height
)
2334 gnt_widget_set_size(ggblist
->window
, width
, height
);