# HG changeset patch # User Sadrul Habib Chowdhury # Date 1200987481 0 # Node ID 2c9646c55369328a5b7da6864ce82d93ef2baf26 # Parent 364e67f1a429ed80d85cb7029c0f12cc08119f95 Add a buddy list manager for finch. This allows plugins to show the buddylist in 'Online/Offline' grouping, account-specific grouping, no-grouping, or even multi-level grouping. The same can be done for Pidgin. I think it'd be cool if someone does that. diff -r 364e67f1a429 -r 2c9646c55369 finch/gntblist.c --- a/finch/gntblist.c Tue Jan 22 07:28:11 2008 +0000 +++ b/finch/gntblist.c Tue Jan 22 07:38:01 2008 +0000 @@ -81,6 +81,8 @@ /* These are the menuitems that get regenerated */ GntMenuItem *accounts; GntMenuItem *plugins; + + FinchBlistManager *manager; } FinchBlist; typedef struct @@ -115,7 +117,10 @@ static void add_chat(PurpleChat *chat, FinchBlist *ggblist); static void add_node(PurpleBlistNode *node, FinchBlist *ggblist); static void node_update(PurpleBuddyList *list, PurpleBlistNode *node); +static gboolean is_contact_online(PurpleContact *contact); +static gboolean is_group_online(PurpleGroup *group); static void draw_tooltip(FinchBlist *ggblist); +static void tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full); static gboolean remove_typing_cb(gpointer null); static void remove_peripherals(FinchBlist *ggblist); static const char * get_display_name(PurpleBlistNode *node); @@ -137,6 +142,165 @@ static int color_offline; static int color_idle; +/** + * Buddy List Manager functions. + */ + +static gboolean default_can_add_node(PurpleBlistNode *node) +{ + gboolean offline = purple_prefs_get_bool(PREF_ROOT "/showoffline"); + + if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { + PurpleBuddy *buddy = (PurpleBuddy*)node; + if (!purple_buddy_get_contact(buddy)) + return FALSE; /* When a new buddy is added and show-offline is set */ + if (PURPLE_BUDDY_IS_ONLINE(buddy)) + return TRUE; /* The buddy is online */ + if (!purple_account_is_connected(purple_buddy_get_account(buddy))) + return FALSE; /* The account is disconnected. Do not show */ + if (offline) + return TRUE; /* We want to see offline buddies too */ + } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { + PurpleContact *contact = (PurpleContact*)node; + if (contact->currentsize < 1) + return FALSE; /* No online accounts in this contact */ + if (!offline && !is_contact_online(contact)) + return FALSE; /* Don't want to see offline buddies */ + return TRUE; + } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { + PurpleChat *chat = (PurpleChat*)node; + if (purple_account_is_connected(purple_chat_get_account(chat))) + return TRUE; /* Show whenever the account is online */ + } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { + PurpleGroup *group = (PurpleGroup*)node; + gboolean empty = purple_prefs_get_bool(PREF_ROOT "/emptygroups"); + if (empty) + return TRUE; /* If we want to see empty groups, we can show any group */ + + if (group->currentsize < 1) + return FALSE; /* No online accounts for this group */ + + if (!offline && !is_group_online(group)) + return FALSE; /* Do not want to see group only with offline buddies */ + + return TRUE; + } + + return FALSE; +} + +static gpointer default_find_parent(PurpleBlistNode *node) +{ + gpointer ret = NULL; + switch (purple_blist_node_get_type(node)) { + case PURPLE_BLIST_BUDDY_NODE: + case PURPLE_BLIST_CONTACT_NODE: + case PURPLE_BLIST_CHAT_NODE: + ret = purple_blist_node_get_parent(node); + break; + default: + break; + } + if (ret) + add_node(ret, ggblist); + return ret; +} + +static gboolean default_create_tooltip(gpointer selected_row, GString **body, char **tool_title) +{ + GString *str; + PurpleBlistNode *node = selected_row; + int lastseen = 0; + char *title; + + if (!node || + purple_blist_node_get_type(node) == PURPLE_BLIST_OTHER_NODE) + return FALSE; + + str = g_string_new(""); + + if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { + PurpleBuddy *pr = purple_contact_get_priority_buddy((PurpleContact*)node); + gboolean offline = !PURPLE_BUDDY_IS_ONLINE(pr); + gboolean showoffline = purple_prefs_get_bool(PREF_ROOT "/showoffline"); + const char *name = purple_buddy_get_name(pr); + + title = g_strdup(name); + tooltip_for_buddy(pr, str, TRUE); + for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node)) { + PurpleBuddy *buddy = (PurpleBuddy*)node; + if (offline) { + int value = purple_blist_node_get_int(node, "last_seen"); + if (value > lastseen) + lastseen = value; + } + if (node == (PurpleBlistNode*)pr) + continue; + if (!purple_account_is_connected(buddy->account)) + continue; + if (!showoffline && !PURPLE_BUDDY_IS_ONLINE(buddy)) + continue; + str = g_string_append(str, "\n----------\n"); + tooltip_for_buddy(buddy, str, FALSE); + } + } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { + PurpleBuddy *buddy = (PurpleBuddy *)node; + tooltip_for_buddy(buddy, str, TRUE); + title = g_strdup(purple_buddy_get_name(buddy)); + if (!PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node)) + lastseen = purple_blist_node_get_int(node, "last_seen"); + } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { + PurpleGroup *group = (PurpleGroup *)node; + + g_string_append_printf(str, _("Online: %d\nTotal: %d"), + purple_blist_get_group_online_count(group), + purple_blist_get_group_size(group, FALSE)); + + title = g_strdup(group->name); + } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { + PurpleChat *chat = (PurpleChat *)node; + PurpleAccount *account = chat->account; + + g_string_append_printf(str, _("Account: %s (%s)"), + purple_account_get_username(account), + purple_account_get_protocol_name(account)); + + title = g_strdup(purple_chat_get_name(chat)); + } else { + g_string_free(str, TRUE); + return FALSE; + } + + if (lastseen > 0) { + char *tmp = purple_str_seconds_to_string(time(NULL) - lastseen); + g_string_append_printf(str, _("\nLast Seen: %s ago"), tmp); + g_free(tmp); + } + + if (tool_title) + *tool_title = title; + else + g_free(title); + + if (body) + *body = str; + else + g_string_free(str, TRUE); + + return TRUE; +} + +static FinchBlistManager default_manager = +{ + "default", + N_("Default"), + default_can_add_node, + default_find_parent, + default_create_tooltip, + {NULL, NULL, NULL, NULL} +}; +static GList *managers; + static FinchBlistNode * create_finch_blist_node(PurpleBlistNode *node, gpointer row) { @@ -248,8 +412,12 @@ { } -static void add_node(PurpleBlistNode *node, FinchBlist *ggblist) +static void +add_node(PurpleBlistNode *node, FinchBlist *ggblist) { + if (!ggblist->manager->can_add_node(node)) + return; + if (PURPLE_BLIST_NODE_IS_BUDDY(node)) add_buddy((PurpleBuddy*)node, ggblist); else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) @@ -261,6 +429,11 @@ draw_tooltip(ggblist); } +void finch_blist_manager_add_node(PurpleBlistNode *node) +{ + add_node(node, ggblist); +} + static void remove_tooltip(FinchBlist *ggblist) { @@ -323,26 +496,22 @@ 0, get_display_name(node)); gnt_tree_sort_row(GNT_TREE(ggblist->tree), node); blist_update_row_flags(node); + if (gnt_tree_get_parent_key(GNT_TREE(ggblist->tree), node) != + ggblist->manager->find_parent(node)) + node_remove(list, node); } if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { PurpleBuddy *buddy = (PurpleBuddy*)node; add_node((PurpleBlistNode*)buddy, list->ui_data); - node_update(list, purple_blist_node_get_parent(node)); } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { - add_chat((PurpleChat *)node, list->ui_data); + add_node(node, list->ui_data); } else if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { - PurpleContact *contact = (PurpleContact*)node; - if ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_contact_online(contact)) || - contact->currentsize < 1) - /* nothing */; - else { - if (node->ui_data == NULL) { - /* The core seems to expect the UI to add the buddies. */ - for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node)) - add_node(node, list->ui_data); - } + if (node->ui_data == NULL) { + /* The core seems to expect the UI to add the buddies. */ + for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node)) + add_node(node, list->ui_data); } } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { PurpleGroup *group = (PurpleGroup*)node; @@ -363,6 +532,7 @@ ggblist = g_new0(FinchBlist, 1); list->ui_data = ggblist; + ggblist->manager = &default_manager; } static void @@ -580,11 +750,14 @@ static void add_group(PurpleGroup *group, FinchBlist *ggblist) { + gpointer parent; PurpleBlistNode *node = (PurpleBlistNode *)group; if (node->ui_data) return; + parent = ggblist->manager->find_parent((PurpleBlistNode*)group); create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group, - gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), NULL, NULL)); + gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), + parent, NULL)); gnt_tree_set_expanded(GNT_TREE(ggblist->tree), node, !purple_blist_node_get_bool(node, "collapsed")); } @@ -650,25 +823,24 @@ static void add_chat(PurpleChat *chat, FinchBlist *ggblist) { - PurpleGroup *group; + gpointer parent; PurpleBlistNode *node = (PurpleBlistNode *)chat; if (node->ui_data) return; if (!purple_account_is_connected(chat->account)) return; - group = purple_chat_get_group(chat); - add_node((PurpleBlistNode*)group, ggblist); + parent = ggblist->manager->find_parent((PurpleBlistNode*)chat); create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), chat, gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), - group, NULL)); + parent, NULL)); } static void add_contact(PurpleContact *contact, FinchBlist *ggblist) { - PurpleGroup *group; + gpointer parent; PurpleBlistNode *node = (PurpleBlistNode*)contact; const char *name; @@ -679,12 +851,11 @@ if (name == NULL) return; - group = (PurpleGroup*)purple_blist_node_get_parent(node); - add_node((PurpleBlistNode*)group, ggblist); + parent = ggblist->manager->find_parent((PurpleBlistNode*)contact); create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), contact, gnt_tree_create_row(GNT_TREE(ggblist->tree), name), - group, NULL)); + parent, NULL)); gnt_tree_set_expanded(GNT_TREE(ggblist->tree), contact, FALSE); } @@ -692,26 +863,19 @@ static void add_buddy(PurpleBuddy *buddy, FinchBlist *ggblist) { + gpointer parent; + PurpleBlistNode *node = (PurpleBlistNode *)buddy; PurpleContact *contact; - PurpleBlistNode *node = (PurpleBlistNode *)buddy; if (node->ui_data) return; - if (!purple_account_is_connected(buddy->account)) - return; - - if (!PURPLE_BUDDY_IS_ONLINE(buddy) && !purple_prefs_get_bool(PREF_ROOT "/showoffline")) - return; - - contact = (PurpleContact*)purple_blist_node_get_parent(node); - if (!contact) /* When a new buddy is added and show-offline is set */ - return; - add_node((PurpleBlistNode*)contact, ggblist); + contact = purple_buddy_get_contact(buddy); + parent = ggblist->manager->find_parent((PurpleBlistNode*)buddy); create_finch_blist_node(node, gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy, gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), - contact, NULL)); + parent, NULL)); blist_update_row_flags((PurpleBlistNode*)buddy); if (buddy == purple_contact_get_priority_buddy(contact)) @@ -1221,7 +1385,8 @@ PurpleGroup *tg = NULL; PurpleContact *tc = NULL; - if (target == NULL) + if (target == NULL || + purple_blist_node_get_type(target) == PURPLE_BLIST_OTHER_NODE) return; if (PURPLE_BLIST_NODE_IS_GROUP(target)) @@ -1447,16 +1612,15 @@ { PurpleBlistNode *node; int x, y, top, width, w, h; - GString *str; + GString *str = NULL; GntTree *tree; GntWidget *widget, *box, *tv; char *title = NULL; - int lastseen = 0; widget = ggblist->tree; tree = GNT_TREE(widget); - if (!gnt_widget_has_focus(ggblist->tree) || + if (!gnt_widget_has_focus(ggblist->tree) || (ggblist->context && !GNT_WIDGET_IS_FLAG_SET(ggblist->context, GNT_WIDGET_INVISIBLE))) return FALSE; @@ -1471,65 +1635,8 @@ if (!node) return FALSE; - str = g_string_new(""); - - if (PURPLE_BLIST_NODE_IS_CONTACT(node)) { - PurpleBuddy *pr = purple_contact_get_priority_buddy((PurpleContact*)node); - gboolean offline = !PURPLE_BUDDY_IS_ONLINE(pr); - gboolean showoffline = purple_prefs_get_bool(PREF_ROOT "/showoffline"); - const char *name = purple_buddy_get_name(pr); - - title = g_strdup(name); - tooltip_for_buddy(pr, str, TRUE); - for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node)) { - PurpleBuddy *buddy = (PurpleBuddy*)node; - if (offline) { - int value = purple_blist_node_get_int(node, "last_seen"); - if (value > lastseen) - lastseen = value; - } - if (node == (PurpleBlistNode*)pr) - continue; - if (!purple_account_is_connected(buddy->account)) - continue; - if (!showoffline && !PURPLE_BUDDY_IS_ONLINE(buddy)) - continue; - str = g_string_append(str, "\n----------\n"); - tooltip_for_buddy(buddy, str, FALSE); - } - } else if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { - PurpleBuddy *buddy = (PurpleBuddy *)node; - tooltip_for_buddy(buddy, str, TRUE); - title = g_strdup(purple_buddy_get_name(buddy)); - if (!PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node)) - lastseen = purple_blist_node_get_int(node, "last_seen"); - } else if (PURPLE_BLIST_NODE_IS_GROUP(node)) { - PurpleGroup *group = (PurpleGroup *)node; - - g_string_append_printf(str, _("Online: %d\nTotal: %d"), - purple_blist_get_group_online_count(group), - purple_blist_get_group_size(group, FALSE)); - - title = g_strdup(group->name); - } else if (PURPLE_BLIST_NODE_IS_CHAT(node)) { - PurpleChat *chat = (PurpleChat *)node; - PurpleAccount *account = chat->account; - - g_string_append_printf(str, _("Account: %s (%s)"), - purple_account_get_username(account), - purple_account_get_protocol_name(account)); - - title = g_strdup(purple_chat_get_name(chat)); - } else { - g_string_free(str, TRUE); + if (!ggblist->manager->create_tooltip(node, &str, &title)) return FALSE; - } - - if (lastseen > 0) { - char *tmp = purple_str_seconds_to_string(time(NULL) - lastseen); - g_string_append_printf(str, _("\nLast Seen: %s ago"), tmp); - g_free(tmp); - } gnt_widget_get_position(widget, &x, &y); gnt_widget_get_size(widget, &width, NULL); @@ -1848,6 +1955,9 @@ purple_signal_connect(purple_connections_get_handle(), "signed-on", purple_blist_get_handle(), G_CALLBACK(account_signed_on_cb), NULL); + + finch_blist_install_manager(&default_manager); + return; } @@ -2444,11 +2554,33 @@ } static void +menu_group_set_cb(GntMenuItem *item, gpointer null) +{ + const char *id = g_object_get_data(G_OBJECT(item), "grouping-id"); + FinchBlistManager *manager; + + manager = finch_blist_manager_find(id); + if (!manager) + return; + + ggblist->manager = manager; + if (ggblist->manager->can_add_node == NULL) + ggblist->manager->can_add_node = default_can_add_node; + if (ggblist->manager->find_parent == NULL) + ggblist->manager->find_parent = default_find_parent; + if (ggblist->manager->create_tooltip == NULL) + ggblist->manager->create_tooltip = default_create_tooltip; + + redraw_blist(NULL, 0, NULL, NULL); +} + +static void create_menu(void) { GntWidget *menu, *sub, *subsub; GntMenuItem *item; GntWindow *window; + GList *iter; if (!ggblist) return; @@ -2533,6 +2665,23 @@ gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(item, menu_add_group_cb, NULL); + item = gnt_menuitem_new(_("Grouping")); + gnt_menu_add_item(GNT_MENU(sub), item); + + subsub = gnt_menu_new(GNT_MENU_POPUP); + gnt_menuitem_set_submenu(item, GNT_MENU(subsub)); + + for (iter = managers; iter; iter = iter->next) { + char menuid[128]; + FinchBlistManager *manager = iter->data; + item = gnt_menuitem_new(_(manager->name)); + snprintf(menuid, sizeof(menuid), "grouping-%s", manager->id); + gnt_menuitem_set_id(GNT_MENU_ITEM(item), menuid); + gnt_menu_add_item(GNT_MENU(subsub), item); + g_object_set_data_full(G_OBJECT(item), "grouping-id", g_strdup(manager->id), g_free); + gnt_menuitem_set_callback(item, menu_group_set_cb, NULL); + } + reconstruct_accounts_menu(); gnt_menu_add_item(GNT_MENU(menu), ggblist->accounts); @@ -2684,3 +2833,34 @@ { gnt_widget_set_size(ggblist->window, width, height); } + +void finch_blist_install_manager(const FinchBlistManager *manager) +{ + if (!g_list_find(managers, manager)) + managers = g_list_append(managers, (gpointer)manager); +} + +void finch_blist_uninstall_manager(const FinchBlistManager *manager) +{ + managers = g_list_remove(managers, manager); +} + +FinchBlistManager * finch_blist_manager_find(const char *id) +{ + GList *iter = managers; + if (!id) + return NULL; + + for (; iter; iter = iter->next) { + FinchBlistManager *m = iter->data; + if (strcmp(id, m->id) == 0) + return m; + } + return NULL; +} + +GntTree * finch_blist_get_tree(void) +{ + return ggblist ? GNT_TREE(ggblist->tree) : NULL; +} + diff -r 364e67f1a429 -r 2c9646c55369 finch/gntblist.h --- a/finch/gntblist.h Tue Jan 22 07:28:11 2008 +0000 +++ b/finch/gntblist.h Tue Jan 22 07:38:01 2008 +0000 @@ -27,12 +27,23 @@ #define _GNT_BLIST_H #include "blist.h" +#include "gnttree.h" /********************************************************************** * @name GNT BuddyList API **********************************************************************/ /*@{*/ +typedef struct +{ + const char *id; /**< An identifier for the manager. */ + const char *name; /**< Displayable name for the manager. */ + gboolean (*can_add_node)(PurpleBlistNode *node); /**< Whether a node should be added to the view. */ + gpointer (*find_parent)(PurpleBlistNode *node); /**< Find the parent row for a node. */ + gboolean (*create_tooltip)(gpointer selected_row, GString **body, char **title); /**< Create tooltip for a selected row. */ + gpointer reserved[4]; +} FinchBlistManager; + /** * Get the ui-functions. * @@ -103,6 +114,47 @@ */ gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name); +/** + * Get the tree list of the buddy list. + * @return The GntTree widget. + * @since 2.4.0 + */ +GntTree * finch_blist_get_tree(void); + +/** + * Add an alternate buddy list manager. + * + * @param manager The alternate buddylist manager. + * @since 2.4.0 + */ +void finch_blist_install_manager(const FinchBlistManager *manager); + +/** + * Remove an alternate buddy list manager. + * + * @param manager The buddy list manager to remove. + * @since 2.4.0 + */ +void finch_blist_uninstall_manager(const FinchBlistManager *manager); + +/** + * Find a buddy list manager. + * + * @param id The identifier for the desired buddy list manager. + * + * @return The manager with the requested identifier, if available. @c NULL otherwise. + * @since 2.4.0 + */ +FinchBlistManager * finch_blist_manager_find(const char *id); + +/** + * Request the active buddy list manager to add a node. + * + * @param node The node to add + * @since 2.4.0 + */ +void finch_blist_manager_add_node(PurpleBlistNode *node); + /*@}*/ #endif