# HG changeset patch # User Sadrul Habib Chowdhury # Date 1201011299 0 # Node ID ca9a660cc388dd0c68e90e0027e6e9d17b69b69e # Parent ce5ced43cd93973ed90ff9d7ef8422847ad0e246# Parent 18ad08694be4c936811799be6c036b6f32a8ec54 merge of '4ca258deda6a50b61f8431bb3a742805c180a583' and 'f61e8366b7d42ee3c6df40b1f37dbdb4a9a1b343' diff -r ce5ced43cd93 -r ca9a660cc388 ChangeLog.API --- a/ChangeLog.API Mon Jan 21 07:32:49 2008 +0000 +++ b/ChangeLog.API Tue Jan 22 14:14:59 2008 +0000 @@ -30,6 +30,11 @@ * purple_attention_type_get_outgoing_desc * purple_attention_type_get_icon_name * purple_attention_type_get_unlocalized_name + * Add some PurpleBuddyListNode accessor functions: + * purple_blist_node_get_parent + * purple_blist_node_get_first_child + * purple_blist_node_get_sibling_next + * purple_chat_get_account Pidgin: Added: @@ -65,6 +70,7 @@ string. * Added gnt_style_get_color to get a color pair from an entry in ~/.gntrc + * Added gnt_tree_get_parent_key to get the key for the parent row. version 2.3.0 (11/24/2007): libpurple: diff -r ce5ced43cd93 -r ca9a660cc388 finch/gntblist.c --- a/finch/gntblist.c Mon Jan 21 07:32:49 2008 +0000 +++ b/finch/gntblist.c Tue Jan 22 14:14:59 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) { @@ -220,7 +384,8 @@ is_contact_online(PurpleContact *contact) { PurpleBlistNode *node; - for (node = ((PurpleBlistNode*)contact)->child; node; node = node->next) { + for (node = purple_blist_node_get_first_child(((PurpleBlistNode*)contact)); node; + node = purple_blist_node_get_sibling_next(node)) { if (PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node)) return TRUE; } @@ -231,7 +396,8 @@ is_group_online(PurpleGroup *group) { PurpleBlistNode *node; - for (node = ((PurpleBlistNode*)group)->child; node; node = node->next) { + for (node = purple_blist_node_get_first_child(((PurpleBlistNode*)group)); node; + node = purple_blist_node_get_sibling_next(node)) { if (PURPLE_BLIST_NODE_IS_CHAT(node) && purple_account_is_connected(((PurpleChat *)node)->account)) return TRUE; @@ -246,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)) @@ -259,6 +429,11 @@ draw_tooltip(ggblist); } +void finch_blist_manager_add_node(PurpleBlistNode *node) +{ + add_node(node, ggblist); +} + static void remove_tooltip(FinchBlist *ggblist) { @@ -281,21 +456,21 @@ ggblist->tagged = g_list_remove(ggblist->tagged, node); if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { - PurpleContact *contact = (PurpleContact*)node->parent; + PurpleContact *contact = (PurpleContact*)purple_blist_node_get_parent(node); if ((!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_contact_online(contact)) || contact->currentsize < 1) node_remove(list, (PurpleBlistNode*)contact); else node_update(list, (PurpleBlistNode*)contact); } else if (!PURPLE_BLIST_NODE_IS_GROUP(node)) { - PurpleGroup *group = (PurpleGroup*)node->parent; + PurpleGroup *group = (PurpleGroup*)purple_blist_node_get_parent(node); if ((group->currentsize < 1 && !purple_prefs_get_bool(PREF_ROOT "/emptygroups")) || (!purple_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group))) - node_remove(list, node->parent); - for (node = node->child; node; node = node->next) + node_remove(list, purple_blist_node_get_parent(node)); + for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node)) reset_blist_node_ui_data(node); } else { - for (node = node->child; node; node = node->next) + for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node)) node_remove(list, node); } @@ -321,28 +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; - if (purple_account_is_connected(buddy->account) && - (PURPLE_BUDDY_IS_ONLINE(buddy) || purple_prefs_get_bool(PREF_ROOT "/showoffline"))) - add_node((PurpleBlistNode*)buddy, list->ui_data); - - node_update(list, node->parent); + 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 = node->child; node; node = node->next) - 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 @@ -399,6 +569,8 @@ purple_blist_add_group(grp, NULL); } + /* XXX: Ask if there's already the same buddy in the same group (#4553) */ + buddy = purple_buddy_new(account, username, alias); purple_blist_add_buddy(buddy, NULL, grp, NULL); purple_account_add_buddy(account, buddy); @@ -578,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")); } @@ -648,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; @@ -677,12 +851,11 @@ if (name == NULL) return; - group = (PurpleGroup*)node->parent; - 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); } @@ -690,23 +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; - - contact = (PurpleContact*)node->parent; - 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)) @@ -1035,8 +1204,8 @@ PurpleGroup *group; cnode = (PurpleBlistNode *)contact; - group = (PurpleGroup*)cnode->parent; - for (bnode = cnode->child; bnode; bnode = bnode->next) { + group = (PurpleGroup*)purple_blist_node_get_parent(cnode); + for (bnode = purple_blist_node_get_first_child(cnode); bnode; bnode = purple_blist_node_get_sibling_next(bnode)) { PurpleBuddy *buddy = (PurpleBuddy*)bnode; if (purple_account_is_connected(buddy->account)) purple_account_remove_buddy(buddy->account, buddy, group); @@ -1104,32 +1273,32 @@ { PurpleBlistNode *cnode, *bnode; - cnode = ((PurpleBlistNode*)group)->child; + cnode = purple_blist_node_get_first_child(((PurpleBlistNode*)group)); while (cnode) { if (PURPLE_BLIST_NODE_IS_CONTACT(cnode)) { - bnode = cnode->child; - cnode = cnode->next; + bnode = purple_blist_node_get_first_child(cnode); + cnode = purple_blist_node_get_sibling_next(cnode); while (bnode) { PurpleBuddy *buddy; if (PURPLE_BLIST_NODE_IS_BUDDY(bnode)) { buddy = (PurpleBuddy*)bnode; - bnode = bnode->next; + bnode = purple_blist_node_get_sibling_next(bnode); if (purple_account_is_connected(buddy->account)) { purple_account_remove_buddy(buddy->account, buddy, group); purple_blist_remove_buddy(buddy); } } else { - bnode = bnode->next; + bnode = purple_blist_node_get_sibling_next(bnode); } } } else if (PURPLE_BLIST_NODE_IS_CHAT(cnode)) { PurpleChat *chat = (PurpleChat *)cnode; - cnode = cnode->next; + cnode = purple_blist_node_get_sibling_next(cnode); if (purple_account_is_connected(chat->account)) purple_blist_remove_chat(chat); } else { - cnode = cnode->next; + cnode = purple_blist_node_get_sibling_next(cnode); } } @@ -1216,18 +1385,19 @@ 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)) tg = (PurpleGroup*)target; else if (PURPLE_BLIST_NODE_IS_BUDDY(target)) { - tc = (PurpleContact*)target->parent; - tg = (PurpleGroup*)target->parent->parent; + tc = (PurpleContact*)purple_blist_node_get_parent(target); + tg = (PurpleGroup*)purple_blist_node_get_parent((PurpleBlistNode*)tc); } else { if (PURPLE_BLIST_NODE_IS_CONTACT(target)) tc = (PurpleContact*)target; - tg = (PurpleGroup*)target->parent; + tg = (PurpleGroup*)purple_blist_node_get_parent(target); } if (ggblist->tagged) { @@ -1300,6 +1470,8 @@ tree = GNT_TREE(ggblist->tree); node = gnt_tree_get_selection_data(tree); + if (node && purple_blist_node_get_type(node) == PURPLE_BLIST_OTHER_NODE) + return; if (ggblist->tooltip) remove_tooltip(ggblist); @@ -1442,16 +1614,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; @@ -1466,65 +1637,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 = node->child; node; node = node->next) { - 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); @@ -1843,6 +1957,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; } @@ -2082,7 +2199,7 @@ int log = 0; PurpleBlistNode *node; - for (node = c->child; node; node = node->next) { + for (node = purple_blist_node_get_first_child(c); node; node = purple_blist_node_get_sibling_next(node)) { PurpleBuddy *b = (PurpleBuddy*)node; log += purple_log_get_total_size(PURPLE_LOG_IM, b->name, b->account); } @@ -2176,8 +2293,8 @@ node_remove(purple_get_blist(), node); } else { update_node_display(node, ggblist); - if (node->parent && PURPLE_BLIST_NODE_IS_CONTACT(node->parent)) - update_node_display(node->parent, ggblist); + if (purple_blist_node_get_parent(node) && PURPLE_BLIST_NODE_IS_CONTACT(purple_blist_node_get_parent(node))) + update_node_display(purple_blist_node_get_parent(node), ggblist); } return FALSE; @@ -2195,8 +2312,8 @@ purple_timeout_remove(fnode->signed_timer); fnode->signed_timer = purple_timeout_add_seconds(6, (GSourceFunc)buddy_recent_signed_on_off, data); update_node_display(node, ggblist); - if (node->parent && PURPLE_BLIST_NODE_IS_CONTACT(node->parent)) - update_node_display(node->parent, ggblist); + if (purple_blist_node_get_parent(node) && PURPLE_BLIST_NODE_IS_CONTACT(purple_blist_node_get_parent(node))) + update_node_display(purple_blist_node_get_parent(node), ggblist); return FALSE; } @@ -2439,11 +2556,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; @@ -2513,21 +2652,38 @@ subsub = gnt_menu_new(GNT_MENU_POPUP); gnt_menuitem_set_submenu(item, GNT_MENU(subsub)); - item = gnt_menuitem_new("Buddy"); + item = gnt_menuitem_new(_("Buddy")); gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-buddy"); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(item, menu_add_buddy_cb, NULL); - item = gnt_menuitem_new("Chat"); + item = gnt_menuitem_new(_("Chat")); gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-chat"); gnt_menu_add_item(GNT_MENU(subsub), item); gnt_menuitem_set_callback(item, menu_add_chat_cb, NULL); - item = gnt_menuitem_new("Group"); + item = gnt_menuitem_new(_("Group")); gnt_menuitem_set_id(GNT_MENU_ITEM(item), "add-group"); 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); @@ -2679,3 +2835,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 ce5ced43cd93 -r ca9a660cc388 finch/gntblist.h --- a/finch/gntblist.h Mon Jan 21 07:32:49 2008 +0000 +++ b/finch/gntblist.h Tue Jan 22 14:14:59 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 diff -r ce5ced43cd93 -r ca9a660cc388 finch/libgnt/gnttree.c --- a/finch/libgnt/gnttree.c Mon Jan 21 07:32:49 2008 +0000 +++ b/finch/libgnt/gnttree.c Tue Jan 22 14:14:59 2008 +0000 @@ -1841,3 +1841,9 @@ tree->priv->search_func = func; } +gpointer gnt_tree_get_parent_key(GntTree *tree, gpointer key) +{ + GntTreeRow *row = g_hash_table_lookup(tree->hash, key); + return row ? row->parent : NULL; +} + diff -r ce5ced43cd93 -r ca9a660cc388 finch/libgnt/gnttree.h --- a/finch/libgnt/gnttree.h Mon Jan 21 07:32:49 2008 +0000 +++ b/finch/libgnt/gnttree.h Tue Jan 22 14:14:59 2008 +0000 @@ -575,6 +575,17 @@ void gnt_tree_set_search_function(GntTree *tree, gboolean (*func)(GntTree *tree, gpointer key, const char *search, const char *current)); +/** + * Get the parent key for a row. + * + * @param tree The tree + * @param key The key for the row. + * + * @return The key of the parent row. + * @since 2.4.0 + */ +gpointer gnt_tree_get_parent_key(GntTree *tree, gpointer key); + G_END_DECLS #endif /* GNT_TREE_H */ diff -r ce5ced43cd93 -r ca9a660cc388 finch/plugins/Makefile.am --- a/finch/plugins/Makefile.am Mon Jan 21 07:32:49 2008 +0000 +++ b/finch/plugins/Makefile.am Tue Jan 22 14:14:59 2008 +0000 @@ -1,7 +1,8 @@ gntclipboard_la_LDFLAGS = -module -avoid-version gntgf_la_LDFLAGS = -module -avoid-version gnthistory_la_LDFLAGS = -module -avoid-version -gntlastlog_la_LDFLAGS = -module -avoid-version +gntlastlog_la_LDFLAGS = -module -avoid-version +grouping_la_LDFLAGS = -module -avoid-version if PLUGINS @@ -9,7 +10,8 @@ gntclipboard.la \ gntgf.la \ gnthistory.la \ - gntlastlog.la + gntlastlog.la \ + grouping.la plugindir = $(libdir)/finch @@ -17,6 +19,7 @@ gntgf_la_SOURCES = gntgf.c gnthistory_la_SOURCES = gnthistory.c gntlastlog_la_SOURCES = lastlog.c +grouping_la_SOURCES = grouping.c gntclipboard_la_CFLAGS = $(X11_CFLAGS) gntgf_la_CFLAGS = $(X11_CFLAGS) @@ -25,6 +28,7 @@ gntgf_la_LIBADD = $(GLIB_LIBS) $(X11_LIBS) $(top_builddir)/finch/libgnt/libgnt.la gnthistory_la_LIBADD = $(GLIB_LIBS) gntlastlog_la_LIBADD = $(GLIB_LIBS) +grouping_la_LIBADD = $(GLIB_LIBS) $(top_builddir)/finch/libgnt/libgnt.la endif # PLUGINS diff -r ce5ced43cd93 -r ca9a660cc388 finch/plugins/grouping.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/finch/plugins/grouping.c Tue Jan 22 14:14:59 2008 +0000 @@ -0,0 +1,217 @@ +/** + * @file grouping.c Provides different grouping options. + * + * Copyright (C) 2008 Sadrul Habib Chowdhury + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define PURPLE_PLUGIN + +#include "internal.h" +#include "purple.h" + +#include "gntblist.h" +#include "gntplugin.h" + +#include "gnttree.h" + +/** + * Online/Offline + */ +static PurpleBlistNode online = {.type = PURPLE_BLIST_OTHER_NODE}, + offline = {.type = PURPLE_BLIST_OTHER_NODE}; + +static gboolean on_offline_can_add_node(PurpleBlistNode *node) +{ + switch (purple_blist_node_get_type(node)) { + case PURPLE_BLIST_CONTACT_NODE: + { + PurpleContact *contact = (PurpleContact*)node; + if (contact->currentsize > 0) + return TRUE; + return FALSE; + } + break; + case PURPLE_BLIST_BUDDY_NODE: + { + PurpleBuddy *buddy = (PurpleBuddy*)node; + if (PURPLE_BUDDY_IS_ONLINE(buddy)) + return TRUE; + if (purple_prefs_get_bool("/finch/blist/showoffline") && + purple_account_is_connected(purple_buddy_get_account(buddy))) + return TRUE; + return FALSE; + } + break; + case PURPLE_BLIST_CHAT_NODE: + { + PurpleChat *chat = (PurpleChat*)node; + return purple_account_is_connected(purple_chat_get_account(chat)); + } + break; + default: + return FALSE; + } +} + +static gpointer on_offline_find_parent(PurpleBlistNode *node) +{ + gpointer ret = NULL; + GntTree *tree = finch_blist_get_tree(); + + if (!tree) + return NULL; + + if (!g_list_find(gnt_tree_get_rows(tree), &online)) { + gnt_tree_remove_all(tree); + gnt_tree_add_row_after(tree, &online, + gnt_tree_create_row(tree, _("Online")), NULL, NULL); + gnt_tree_add_row_after(tree, &offline, + gnt_tree_create_row(tree, _("Offline")), NULL, &online); + } + + switch (purple_blist_node_get_type(node)) { + case PURPLE_BLIST_CONTACT_NODE: + node = (PurpleBlistNode*)purple_contact_get_priority_buddy((PurpleContact*)node); + ret = PURPLE_BUDDY_IS_ONLINE((PurpleBuddy*)node) ? &online : &offline; + break; + case PURPLE_BLIST_BUDDY_NODE: + ret = purple_blist_node_get_parent(node); + finch_blist_manager_add_node(ret); + break; + case PURPLE_BLIST_CHAT_NODE: + ret = &online; + break; + default: + break; + } + return ret; +} + +static gboolean on_offline_create_tooltip(gpointer selected_row, GString **body, char **tool_title) +{ + static FinchBlistManager *def = NULL; + PurpleBlistNode *node = selected_row; + + if (def == NULL) + def = finch_blist_manager_find("default"); + + if (purple_blist_node_get_type(node) == PURPLE_BLIST_OTHER_NODE) { + /* There should be some easy way of getting the total online count, + * or total number of chats. Doing a loop here will probably be pretty + * expensive. */ + if (body) + *body = g_string_new(node == &online ? _("Online Buddies") : _("Offline Buddies")); + return TRUE; + } else { + return def ? def->create_tooltip(selected_row, body, tool_title) : FALSE; + } +} + +static FinchBlistManager on_offline = +{ + "on-offline", + N_("Online/Offline"), + on_offline_can_add_node, + on_offline_find_parent, + on_offline_create_tooltip, + {NULL, NULL, NULL, NULL} +}; + +/** + * No Grouping. + */ +static gboolean no_group_can_add_node(PurpleBlistNode *node) +{ + return on_offline_can_add_node(node); /* These happen to be the same */ +} + +static gpointer no_group_find_parent(PurpleBlistNode *node) +{ + gpointer ret = NULL; + + switch (purple_blist_node_get_type(node)) { + case PURPLE_BLIST_BUDDY_NODE: + ret = purple_blist_node_get_parent(node); + finch_blist_manager_add_node(ret); + break; + default: + break; + } + return ret; +} + +static FinchBlistManager no_group = +{ + "no-group", + N_("No Grouping"), + no_group_can_add_node, + no_group_find_parent, + NULL, + {NULL, NULL, NULL, NULL} +}; + +static gboolean +plugin_load(PurplePlugin *plugin) +{ + finch_blist_install_manager(&on_offline); + finch_blist_install_manager(&no_group); + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin) +{ + finch_blist_uninstall_manager(&on_offline); + finch_blist_uninstall_manager(&no_group); + return TRUE; +} + +static PurplePluginInfo info = +{ + PURPLE_PLUGIN_MAGIC, + PURPLE_MAJOR_VERSION, + PURPLE_MINOR_VERSION, + PURPLE_PLUGIN_STANDARD, + FINCH_PLUGIN_TYPE, + 0, + NULL, + PURPLE_PRIORITY_DEFAULT, + "grouping", + N_("Grouping"), + VERSION, + N_("Provides alternate buddylist grouping options."), + N_("Provides alternate buddylist grouping options."), + "Sadrul H Chowdhury ", + PURPLE_WEBSITE, + plugin_load, + plugin_unload, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL,NULL,NULL,NULL +}; + +static void +init_plugin(PurplePlugin *plugin) +{ +} + +PURPLE_INIT_PLUGIN(ignore, init_plugin, info) + + diff -r ce5ced43cd93 -r ca9a660cc388 libpurple/blist.c --- a/libpurple/blist.c Mon Jan 21 07:32:49 2008 +0000 +++ b/libpurple/blist.c Tue Jan 22 14:14:59 2008 +0000 @@ -753,6 +753,21 @@ return ret; } +PurpleBlistNode *purple_blist_node_get_parent(PurpleBlistNode *node) +{ + return node ? node->parent : NULL; +} + +PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node) +{ + return node ? node->child : NULL; +} + +PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node) +{ + return node? node->next : NULL; +} + void purple_blist_update_buddy_status(PurpleBuddy *buddy, PurpleStatus *old_status) { @@ -2232,6 +2247,14 @@ return (PurpleGroup *)(((PurpleBlistNode *)chat)->parent); } +PurpleAccount * +purple_chat_get_account(PurpleChat *chat) +{ + g_return_val_if_fail(chat != NULL, NULL); + + return chat->account; +} + PurpleContact *purple_buddy_get_contact(PurpleBuddy *buddy) { g_return_val_if_fail(buddy != NULL, NULL); diff -r ce5ced43cd93 -r ca9a660cc388 libpurple/blist.h --- a/libpurple/blist.h Mon Jan 21 07:32:49 2008 +0000 +++ b/libpurple/blist.h Tue Jan 22 14:14:59 2008 +0000 @@ -231,10 +231,49 @@ * @param node A node. * @param offline Whether to include nodes for offline accounts * @return The next node + * @see purple_blist_node_get_parent + * @see purple_blist_node_get_first_child + * @see purple_blist_node_get_sibling_next */ PurpleBlistNode *purple_blist_node_next(PurpleBlistNode *node, gboolean offline); /** + * Returns the parent node of a given node. + * + * @param node A node. + * @return The parent node. + * @since 2.4.0 + * @see purple_blist_node_get_first_child + * @see purple_blist_node_get_sibling_next + * @see purple_blist_node_next + */ +PurpleBlistNode *purple_blist_node_get_parent(PurpleBlistNode *node); + +/** + * Returns the the first child node of a given node. + * + * @param node A node. + * @return The child node. + * @since 2.4.0 + * @see purple_blist_node_get_parent + * @see purple_blist_node_get_sibling_next + * @see purple_blist_node_next + */ +PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node); + +/** + * Returns the sibling node of a given node. + * + * @param node A node. + * @return The sibling node. + * @since 2.4.0 + * @see purple_blist_node_get_parent + * @see purple_blist_node_get_first_child + * @see purple_blist_node_next + */ +PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node); + +/** * Shows the buddy list, creating a new one if necessary. */ void purple_blist_show(void); @@ -667,6 +706,16 @@ PurpleGroup *purple_chat_get_group(PurpleChat *chat); /** + * Returns the account the chat belongs to. + * + * @param chat The chat. + * + * @return The account the chat belongs to. + * @since 2.4.0 + */ +PurpleAccount *purple_chat_get_account(PurpleChat *chat); + +/** * Returns the group of which the buddy is a member. * * @param buddy The buddy