pidgin/pidgin

Use Meson summary() function.

2021-07-27, Elliott Sales de Andrade
cb640ea0f315
Use Meson summary() function.

Now that we require at least 0.52, we can use Meson's builtin summary printing to display the results of configuration.

Testing Done:
Configured with defaults, and with pixmaps disabled to trigger the warning: https://asciinema.org/a/mV2oxOoVCJNdmrPwgqqUJ3mkU?t=17

Reviewed at https://reviews.imfreedom.org/r/848/
/*
* finch
*
* Finch is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include NCURSES_HEADER
#include <string.h>
#include <glib/gi18n-lib.h>
#include <gplugin.h>
#include <purple.h>
#include <gnt.h>
#include "gntblist.h"
#include "gntconv.h"
#include "gntlog.h"
#include "gntmenuutil.h"
#include "gntstatus.h"
#define PREF_ROOT "/finch/blist"
#define TYPING_TIMEOUT_S 4
#define UI_DATA "ui-finch"
#define SHOW_EMPTY_GROUP_TIMEOUT 60
struct _FinchBuddyList {
PurpleBuddyList parent;
GntWidget *window;
GntWidget *tree;
GntWidget *tooltip;
PurpleBlistNode *tnode; /* Who is the tooltip being displayed for? */
GList *tagged; /* A list of tagged blistnodes */
GntWidget *context;
PurpleBlistNode *cnode;
/* XXX: I am KISSing */
GntWidget *status; /* Dropdown with the statuses */
GntWidget *statustext; /* Status message */
int typing;
GntWidget *menu;
/* These are the menuitems that get regenerated */
GntMenuItem *accounts;
GntMenuItem *plugins;
GntMenuItem *grouping;
/* When a new group is manually added, it is empty, but we still want to show it
* for a while (SHOW_EMPTY_GROUP_TIMEOUT seconds) even if 'show empty groups' is
* not selected.
*/
GList *new_group;
guint new_group_timeout;
FinchBlistManager *manager;
};
typedef struct
{
gpointer row; /* the row in the GntTree */
guint signed_timer; /* used when 'recently' signed on/off */
} FinchBlistNode;
typedef enum
{
STATUS_PRIMITIVE = 0,
STATUS_SAVED_POPULAR,
STATUS_SAVED_ALL,
STATUS_SAVED_NEW
} StatusType;
typedef struct
{
StatusType type;
union
{
PurpleStatusPrimitive prim;
PurpleSavedStatus *saved;
} u;
} StatusBoxItem;
static FinchBuddyList *ggblist;
static void add_buddy(PurpleBuddy *buddy, FinchBuddyList *ggblist);
static void add_contact(PurpleContact *contact, FinchBuddyList *ggblist);
static void add_group(PurpleGroup *group, FinchBuddyList *ggblist);
static void add_chat(PurpleChat *chat, FinchBuddyList *ggblist);
static void add_node(PurpleBlistNode *node, FinchBuddyList *ggblist);
static void node_update(PurpleBuddyList *list, PurpleBlistNode *node);
static void draw_tooltip(FinchBuddyList *ggblist);
static void tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full);
static gboolean remove_typing_cb(gpointer null);
static void remove_peripherals(FinchBuddyList *ggblist);
static const char * get_display_name(PurpleBlistNode *node);
static void savedstatus_changed(PurpleSavedStatus *now, PurpleSavedStatus *old);
static void blist_show(PurpleBuddyList *list);
static void update_node_display(PurpleBlistNode *buddy,
FinchBuddyList *ggblist);
static void update_buddy_display(PurpleBuddy *buddy, FinchBuddyList *ggblist);
static gboolean account_autojoin_cb(PurpleConnection *pc, gpointer null);
static void finch_request_add_buddy(PurpleBuddyList *list,
PurpleAccount *account,
const char *username, const char *grp,
const char *alias);
static void menu_group_set_cb(GntMenuItem *item, gpointer null);
/* Sort functions */
static int blist_node_compare_position(PurpleBlistNode *n1, PurpleBlistNode *n2);
static int blist_node_compare_text(PurpleBlistNode *n1, PurpleBlistNode *n2);
static int blist_node_compare_status(PurpleBlistNode *n1, PurpleBlistNode *n2);
static int blist_node_compare_log(PurpleBlistNode *n1, PurpleBlistNode *n2);
static int color_available;
static int color_away;
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_IS_BUDDY(node)) {
PurpleBuddy *buddy = (PurpleBuddy*)node;
FinchBlistNode *fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
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 */
if (fnode && fnode->signed_timer)
return TRUE; /* Show if the buddy just signed off */
if (purple_blist_node_get_bool(node, "show_offline"))
return TRUE;
} else if (PURPLE_IS_CONTACT(node)) {
PurpleBlistNode *nd;
for (nd = purple_blist_node_get_first_child(node);
nd; nd = purple_blist_node_get_sibling_next(nd)) {
if (default_can_add_node(nd))
return TRUE;
}
} else if (PURPLE_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_IS_GROUP(node)) {
PurpleBlistNode *nd;
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 */
for (nd = purple_blist_node_get_first_child(node);
nd; nd = purple_blist_node_get_sibling_next(nd)) {
if (default_can_add_node(nd))
return TRUE;
}
if (ggblist && ggblist->new_group && g_list_find(ggblist->new_group, node))
return TRUE;
}
return FALSE;
}
static gpointer default_find_parent(PurpleBlistNode *node)
{
gpointer ret = NULL;
if (PURPLE_IS_BUDDY(node) || PURPLE_IS_CONTACT(node) || PURPLE_IS_CHAT(node))
ret = purple_blist_node_get_parent(node);
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;
str = g_string_new("");
if (PURPLE_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(purple_buddy_get_account(buddy)))
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_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_IS_GROUP(node)) {
PurpleGroup *group = (PurpleGroup *)node;
g_string_append_printf(str, _("Online: %d\nTotal: %d"),
purple_counting_node_get_online_count(PURPLE_COUNTING_NODE(group)),
purple_counting_node_get_current_size(PURPLE_COUNTING_NODE(group)));
title = g_strdup(purple_group_get_name(group));
} else if (PURPLE_IS_CHAT(node)) {
PurpleChat *chat = (PurpleChat *)node;
PurpleAccount *account = purple_chat_get_account(chat);
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"),
NULL,
NULL,
default_can_add_node,
default_find_parent,
default_create_tooltip,
{NULL, NULL, NULL, NULL}
};
static GList *managers;
static void
finch_blist_node_free(FinchBlistNode *node) {
if(node->signed_timer) {
g_source_remove(node->signed_timer);
}
g_free(node);
}
static FinchBlistNode *
create_finch_blist_node(PurpleBlistNode *node, gpointer row)
{
FinchBlistNode *fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
if (!fnode) {
fnode = g_new0(FinchBlistNode, 1);
fnode->signed_timer = 0;
g_object_set_data_full(G_OBJECT(node), UI_DATA, fnode,
(GDestroyNotify)finch_blist_node_free);
}
fnode->row = row;
return fnode;
}
static int
get_display_color(PurpleBlistNode *node)
{
PurpleBuddy *buddy;
int color = 0;
if (PURPLE_IS_CONTACT(node))
node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
if (!PURPLE_IS_BUDDY(node))
return 0;
buddy = (PurpleBuddy*)node;
if (purple_presence_is_idle(purple_buddy_get_presence(buddy))) {
color = color_idle;
} else if (purple_presence_is_available(purple_buddy_get_presence(buddy))) {
color = color_available;
} else if (purple_presence_is_online(purple_buddy_get_presence(buddy)) &&
!purple_presence_is_available(purple_buddy_get_presence(buddy))) {
color = color_away;
} else if (!purple_presence_is_online(purple_buddy_get_presence(buddy))) {
color = color_offline;
}
return color;
}
static GntTextFormatFlags
get_blist_node_flag(FinchBuddyList *ggblist, PurpleBlistNode *node)
{
GntTextFormatFlags flag = 0;
FinchBlistNode *fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
if (ggblist->tagged && g_list_find(ggblist->tagged, node))
flag |= GNT_TEXT_FLAG_BOLD;
if (fnode && fnode->signed_timer)
flag |= GNT_TEXT_FLAG_BLINK;
else if (PURPLE_IS_CONTACT(node)) {
node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
if (fnode && fnode->signed_timer)
flag |= GNT_TEXT_FLAG_BLINK;
}
return flag;
}
static void
blist_update_row_flags(FinchBuddyList *ggblist, PurpleBlistNode *node)
{
gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node,
get_blist_node_flag(ggblist, node));
gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node));
}
static void
new_node(PurpleBuddyList *list, PurpleBlistNode *node)
{
}
static void
add_node(PurpleBlistNode *node, FinchBuddyList *ggblist)
{
if(g_object_get_data(G_OBJECT(node), UI_DATA)) {
return;
}
if(!ggblist->manager->can_add_node(node)) {
return;
}
if(PURPLE_IS_BUDDY(node)) {
add_buddy((PurpleBuddy*)node, ggblist);
} else if (PURPLE_IS_CONTACT(node)) {
add_contact((PurpleContact*)node, ggblist);
} else if (PURPLE_IS_GROUP(node)) {
add_group((PurpleGroup*)node, ggblist);
} else if (PURPLE_IS_CHAT(node)) {
add_chat((PurpleChat *)node, ggblist);
}
draw_tooltip(ggblist);
}
void finch_blist_manager_add_node(PurpleBlistNode *node)
{
add_node(node, ggblist);
}
static void
remove_tooltip(FinchBuddyList *ggblist)
{
gnt_widget_destroy(ggblist->tooltip);
ggblist->tooltip = NULL;
ggblist->tnode = NULL;
}
static void
node_remove(PurpleBuddyList *list, PurpleBlistNode *node)
{
FinchBuddyList *ggblist = FINCH_BUDDY_LIST(list);
PurpleBlistNode *parent;
if (ggblist == NULL || g_object_get_data(G_OBJECT(node), UI_DATA) == NULL)
return;
if (PURPLE_IS_GROUP(node) && ggblist->new_group) {
ggblist->new_group = g_list_remove(ggblist->new_group, node);
}
gnt_tree_remove(GNT_TREE(ggblist->tree), node);
if (ggblist->tagged)
ggblist->tagged = g_list_remove(ggblist->tagged, node);
parent = purple_blist_node_get_parent(node);
for (node = purple_blist_node_get_first_child(node); node;
node = purple_blist_node_get_sibling_next(node))
node_remove(list, node);
if (parent) {
if (!ggblist->manager->can_add_node(parent))
node_remove(list, parent);
else
node_update(list, parent);
}
draw_tooltip(ggblist);
}
static void
node_update(PurpleBuddyList *list, PurpleBlistNode *node)
{
FinchBuddyList *ggblist;
g_return_if_fail(FINCH_IS_BUDDY_LIST(list));
/* It really looks like this should never happen ... but it does.
This will at least emit a warning to the log when it
happens, so maybe someone will figure it out. */
g_return_if_fail(node != NULL);
ggblist = FINCH_BUDDY_LIST(list);
if (ggblist->window == NULL) {
return;
}
if(g_object_get_data(G_OBJECT(node), UI_DATA) != NULL) {
gnt_tree_change_text(GNT_TREE(ggblist->tree), node, 0,
get_display_name(node));
gnt_tree_sort_row(GNT_TREE(ggblist->tree), node);
blist_update_row_flags(ggblist, node);
if (gnt_tree_get_parent_key(GNT_TREE(ggblist->tree), node) !=
ggblist->manager->find_parent(node))
{
node_remove(list, node);
}
}
if (PURPLE_IS_BUDDY(node)) {
PurpleBuddy *buddy = (PurpleBuddy*)node;
add_node((PurpleBlistNode *)buddy, FINCH_BUDDY_LIST(list));
node_update(list, purple_blist_node_get_parent(node));
} else if (PURPLE_IS_CHAT(node)) {
add_node(node, FINCH_BUDDY_LIST(list));
} else if (PURPLE_IS_CONTACT(node)) {
if (g_object_get_data(G_OBJECT(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, FINCH_BUDDY_LIST(list));
}
} else if (PURPLE_IS_GROUP(node)) {
if (!ggblist->manager->can_add_node(node))
node_remove(list, node);
else
add_node(node, FINCH_BUDDY_LIST(list));
}
if (ggblist->tnode == node) {
draw_tooltip(ggblist);
}
}
static gboolean
remove_new_empty_group(gpointer data)
{
PurpleBuddyList *list;
FinchBuddyList *ggblist;
list = purple_blist_get_default();
g_return_val_if_fail(list, FALSE);
ggblist = FINCH_BUDDY_LIST(list);
ggblist->new_group_timeout = 0;
while (ggblist->new_group) {
PurpleBlistNode *group = ggblist->new_group->data;
ggblist->new_group = g_list_delete_link(ggblist->new_group, ggblist->new_group);
node_update(list, group);
}
return FALSE;
}
static void
add_buddy_cb(void *data, PurpleRequestFields *allfields)
{
const char *username = purple_request_fields_get_string(allfields, "screenname");
const char *alias = purple_request_fields_get_string(allfields, "alias");
const char *group = purple_request_fields_get_string(allfields, "group");
const char *invite = purple_request_fields_get_string(allfields, "invite");
PurpleAccount *account = purple_request_fields_get_account(allfields, "account");
const char *error = NULL;
PurpleGroup *grp;
PurpleBuddy *buddy;
if (!username)
error = _("You must provide a username for the buddy.");
else if (!group)
error = _("You must provide a group.");
else if (!account)
error = _("You must select an account.");
else if (!purple_account_is_connected(account))
error = _("The selected account is not online.");
if (error)
{
finch_request_add_buddy(purple_blist_get_default(), account,
username, group, alias);
purple_notify_error(NULL, _("Error"), _("Error adding buddy"),
error, purple_request_cpar_from_account(account));
return;
}
grp = purple_blist_find_group(group);
if (!grp)
{
grp = purple_group_new(group);
purple_blist_add_group(grp, NULL);
}
/* XXX: Ask to merge if there's already a buddy with the same alias in the same group (#4553) */
if ((buddy = purple_blist_find_buddy_in_group(account, username, grp)) == NULL)
{
buddy = purple_buddy_new(account, username, alias);
purple_blist_add_buddy(buddy, NULL, grp, NULL);
}
purple_account_add_buddy(account, buddy, invite);
}
static void
finch_request_add_buddy(PurpleBuddyList *list, PurpleAccount *account,
const char *username, const char *grp,
const char *alias)
{
PurpleRequestFields *fields = purple_request_fields_new();
PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
PurpleRequestField *field;
purple_request_fields_add_group(fields, group);
field = purple_request_field_string_new("screenname", _("Username"), username, FALSE);
purple_request_field_group_add_field(group, field);
field = purple_request_field_string_new("alias", _("Alias (optional)"), alias, FALSE);
purple_request_field_group_add_field(group, field);
field = purple_request_field_string_new("invite", _("Invite message (optional)"), NULL, FALSE);
purple_request_field_group_add_field(group, field);
field = purple_request_field_string_new("group", _("Add in group"), grp, FALSE);
purple_request_field_group_add_field(group, field);
purple_request_field_set_type_hint(field, "group");
field = purple_request_field_account_new("account", _("Account"), NULL);
purple_request_field_account_set_show_all(field, FALSE);
if (account)
purple_request_field_account_set_value(field, account);
purple_request_field_group_add_field(group, field);
purple_request_fields(NULL, _("Add Buddy"), NULL, _("Please enter buddy information."),
fields,
_("Add"), G_CALLBACK(add_buddy_cb),
_("Cancel"), NULL,
purple_request_cpar_from_account(account),
NULL);
}
static void
join_chat(PurpleChat *chat)
{
PurpleAccount *account = purple_chat_get_account(chat);
PurpleConversationManager *manager;
const char *name;
PurpleConversation *conv;
name = purple_chat_get_name_only(chat);
manager = purple_conversation_manager_get_default();
conv = purple_conversation_manager_find_chat(manager, account, name);
if (!conv || purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv))) {
purple_serv_join_chat(purple_account_get_connection(account),
purple_chat_get_components(chat));
} else if (conv) {
purple_conversation_present(conv);
}
}
static void
add_chat_cb(void *data, PurpleRequestFields *allfields)
{
PurpleAccount *account;
const char *alias, *name, *group;
PurpleChat *chat;
PurpleGroup *grp;
GHashTable *hash = NULL;
PurpleConnection *gc;
gboolean autojoin;
PurpleProtocol *protocol;
account = purple_request_fields_get_account(allfields, "account");
name = purple_request_fields_get_string(allfields, "name");
alias = purple_request_fields_get_string(allfields, "alias");
group = purple_request_fields_get_string(allfields, "group");
autojoin = purple_request_fields_get_bool(allfields, "autojoin");
if (!purple_account_is_connected(account) || !name || !*name)
return;
if (!group || !*group)
group = _("Chats");
gc = purple_account_get_connection(account);
protocol = purple_connection_get_protocol(gc);
hash = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc, name);
chat = purple_chat_new(account, name, hash);
if (chat != NULL) {
if ((grp = purple_blist_find_group(group)) == NULL) {
grp = purple_group_new(group);
purple_blist_add_group(grp, NULL);
}
purple_blist_add_chat(chat, grp, NULL);
purple_chat_set_alias(chat, alias);
purple_blist_node_set_bool((PurpleBlistNode*)chat, "gnt-autojoin", autojoin);
if (autojoin) {
join_chat(chat);
}
}
}
static void
finch_request_add_chat(PurpleBuddyList *list, PurpleAccount *account,
PurpleGroup *grp, const char *alias, const char *name)
{
PurpleRequestFields *fields = purple_request_fields_new();
PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
PurpleRequestField *field;
purple_request_fields_add_group(fields, group);
field = purple_request_field_account_new("account", _("Account"), NULL);
purple_request_field_account_set_show_all(field, FALSE);
if (account)
purple_request_field_account_set_value(field, account);
purple_request_field_group_add_field(group, field);
field = purple_request_field_string_new("name", _("Name"), name, FALSE);
purple_request_field_group_add_field(group, field);
field = purple_request_field_string_new("alias", _("Alias"), alias, FALSE);
purple_request_field_group_add_field(group, field);
field = purple_request_field_string_new("group", _("Group"), grp ? purple_group_get_name(grp) : NULL, FALSE);
purple_request_field_group_add_field(group, field);
purple_request_field_set_type_hint(field, "group");
field = purple_request_field_bool_new("autojoin", _("Auto-join"), FALSE);
purple_request_field_group_add_field(group, field);
purple_request_fields(NULL, _("Add Chat"), NULL,
_("You can edit more information from the context menu later."),
fields, _("Add"), G_CALLBACK(add_chat_cb), _("Cancel"), NULL,
NULL, NULL);
}
static void
add_group_cb(FinchBuddyList *ggblist, const char *group)
{
PurpleGroup *grp;
if (!group || !*group) {
purple_notify_error(NULL, _("Error"), _("Error adding group"),
_("You must give a name for the group to add."), NULL);
g_object_unref(ggblist);
return;
}
grp = purple_blist_find_group(group);
if (!grp) {
grp = purple_group_new(group);
purple_blist_add_group(grp, NULL);
}
/* Treat the group as a new group even if it had existed before. This should
* make things easier to add buddies to empty groups (new or old) without having
* to turn on 'show empty groups' setting */
ggblist->new_group = g_list_prepend(ggblist->new_group, grp);
if (ggblist->new_group_timeout)
g_source_remove(ggblist->new_group_timeout);
ggblist->new_group_timeout = g_timeout_add_seconds(SHOW_EMPTY_GROUP_TIMEOUT,
remove_new_empty_group, NULL);
/* Select the group */
if (ggblist->tree) {
FinchBlistNode *fnode = g_object_get_data(G_OBJECT(grp), UI_DATA);
if (!fnode)
add_node((PurpleBlistNode*)grp, ggblist);
gnt_tree_set_selected(GNT_TREE(ggblist->tree), grp);
}
g_object_unref(ggblist);
}
static void
finch_request_add_group(PurpleBuddyList *list)
{
purple_request_input(NULL, _("Add Group"), NULL,
_("Enter the name of the group"), NULL, FALSE,
FALSE, NULL, _("Add"), G_CALLBACK(add_group_cb),
_("Cancel"), G_CALLBACK(g_object_unref), NULL,
g_object_ref(list));
}
static gpointer
finch_blist_get_handle(void)
{
static int handle;
return &handle;
}
static void
add_group(PurpleGroup *group, FinchBuddyList *ggblist)
{
gpointer parent;
PurpleBlistNode *node = (PurpleBlistNode *)group;
if(g_object_get_data(G_OBJECT(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)),
parent, NULL));
gnt_tree_set_expanded(GNT_TREE(ggblist->tree), node,
!purple_blist_node_get_bool(node, "collapsed"));
}
static const char *
get_display_name(PurpleBlistNode *node)
{
static char text[2096];
char status[8] = " ";
const char *name = NULL;
if (PURPLE_IS_CONTACT(node))
node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node))); /* XXX: this can return NULL?! */
if (node == NULL)
return NULL;
if (PURPLE_IS_BUDDY(node))
{
PurpleBuddy *buddy = (PurpleBuddy *)node;
PurpleStatusPrimitive prim;
PurplePresence *presence;
PurpleStatus *now;
gboolean ascii = gnt_ascii_only();
presence = purple_buddy_get_presence(buddy);
if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE))
strncpy(status, ascii ? ":" : "☎", sizeof(status) - 1);
else {
now = purple_presence_get_active_status(presence);
prim = purple_status_type_get_primitive(purple_status_get_status_type(now));
switch(prim) {
case PURPLE_STATUS_OFFLINE:
strncpy(status, ascii ? "x" : "⊗", sizeof(status) - 1);
break;
case PURPLE_STATUS_AVAILABLE:
strncpy(status, ascii ? "o" : "â—¯", sizeof(status) - 1);
break;
default:
strncpy(status, ascii ? "." : "⊖", sizeof(status) - 1);
break;
}
}
name = purple_buddy_get_alias(buddy);
}
else if (PURPLE_IS_CHAT(node))
{
PurpleChat *chat = (PurpleChat*)node;
name = purple_chat_get_name(chat);
strncpy(status, "~", sizeof(status) - 1);
}
else if (PURPLE_IS_GROUP(node))
return purple_group_get_name((PurpleGroup*)node);
g_snprintf(text, sizeof(text) - 1, "%s %s", status, name);
return text;
}
static void
add_chat(PurpleChat *chat, FinchBuddyList *ggblist)
{
gpointer parent;
PurpleBlistNode *node = (PurpleBlistNode *)chat;
if(g_object_get_data(G_OBJECT(node), UI_DATA)) {
return;
}
if(!purple_account_is_connected(purple_chat_get_account(chat))) {
return;
}
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)),
parent, NULL));
}
static void
add_contact(PurpleContact *contact, FinchBuddyList *ggblist)
{
gpointer parent;
PurpleBlistNode *node = (PurpleBlistNode*)contact;
const char *name;
if(g_object_get_data(G_OBJECT(node), UI_DATA)) {
return;
}
name = get_display_name(node);
if (name == NULL)
return;
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),
parent, NULL));
gnt_tree_set_expanded(GNT_TREE(ggblist->tree), contact, FALSE);
}
static void
add_buddy(PurpleBuddy *buddy, FinchBuddyList *ggblist)
{
gpointer parent;
PurpleBlistNode *node = (PurpleBlistNode *)buddy;
PurpleContact *contact;
if(g_object_get_data(G_OBJECT(node), UI_DATA)) {
return;
}
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)),
parent, NULL));
blist_update_row_flags(ggblist, (PurpleBlistNode *)buddy);
if (buddy == purple_contact_get_priority_buddy(contact)) {
blist_update_row_flags(ggblist, (PurpleBlistNode *)contact);
}
}
static void
selection_activate(GntWidget *widget, FinchBuddyList *ggblist)
{
GntTree *tree = GNT_TREE(ggblist->tree);
PurpleBlistNode *node = gnt_tree_get_selection_data(tree);
if (!node)
return;
if (PURPLE_IS_CONTACT(node))
node = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
if (PURPLE_IS_BUDDY(node))
{
PurpleBuddy *buddy = (PurpleBuddy *)node;
PurpleConversation *im;
PurpleConversationManager *manager;
manager = purple_conversation_manager_get_default();
im = purple_conversation_manager_find_im(manager,
purple_buddy_get_account(buddy),
purple_buddy_get_name(buddy));
if(!PURPLE_IS_IM_CONVERSATION(im)) {
im = purple_im_conversation_new(purple_buddy_get_account(buddy),
purple_buddy_get_name(buddy));
} else {
FinchConv *ggconv = FINCH_CONV(im);
gnt_window_present(ggconv->window);
}
finch_conversation_set_active(im);
}
else if (PURPLE_IS_CHAT(node))
{
join_chat((PurpleChat*)node);
}
}
static void
append_proto_menu(GntMenu *menu, PurpleConnection *gc, PurpleBlistNode *node)
{
GList *list;
PurpleProtocol *protocol = purple_connection_get_protocol(gc);
if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, blist_node_menu)) {
return;
}
for(list = purple_protocol_client_blist_node_menu(PURPLE_PROTOCOL_CLIENT(protocol), node);
list; list = g_list_delete_link(list, list))
{
PurpleActionMenu *act = (PurpleActionMenu *) list->data;
if (!act)
continue;
purple_action_menu_set_data(act, node);
finch_append_menu_action(menu, act, node);
}
}
static void
add_custom_action(GntMenu *menu, const char *label, PurpleCallback callback,
gpointer data)
{
PurpleActionMenu *action = purple_action_menu_new(label, callback, data, NULL);
finch_append_menu_action(menu, action, NULL);
}
static void
chat_components_edit_ok(PurpleChat *chat, PurpleRequestFields *allfields)
{
GList *groups, *fields;
for (groups = purple_request_fields_get_groups(allfields); groups; groups = groups->next) {
fields = purple_request_field_group_get_fields(groups->data);
for (; fields; fields = fields->next) {
PurpleRequestField *field = fields->data;
const char *id;
char *val;
id = purple_request_field_get_id(field);
if (purple_request_field_get_field_type(field) == PURPLE_REQUEST_FIELD_INTEGER)
val = g_strdup_printf("%d", purple_request_field_int_get_value(field));
else
val = g_strdup(purple_request_field_string_get_value(field));
if (!val) {
g_hash_table_remove(purple_chat_get_components(chat), id);
} else {
g_hash_table_replace(purple_chat_get_components(chat), g_strdup(id), val); /* val should not be free'd */
}
}
}
}
static void
chat_components_edit(PurpleBlistNode *selected, PurpleChat *chat)
{
PurpleRequestFields *fields = purple_request_fields_new();
PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
PurpleRequestField *field;
GList *parts, *iter;
PurpleProtocol *protocol;
PurpleProtocolChatEntry *pce;
PurpleConnection *gc;
purple_request_fields_add_group(fields, group);
gc = purple_account_get_connection(purple_chat_get_account(chat));
protocol = purple_connection_get_protocol(gc);
parts = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol), gc);
for (iter = parts; iter; iter = iter->next) {
pce = iter->data;
if (pce->is_int) {
int val;
const char *str = g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier);
if (!str || sscanf(str, "%d", &val) != 1)
val = pce->min;
field = purple_request_field_int_new(pce->identifier, pce->label, val, INT_MIN, INT_MAX);
} else {
field = purple_request_field_string_new(pce->identifier, pce->label,
g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier), FALSE);
if (pce->secret)
purple_request_field_string_set_masked(field, TRUE);
}
if (pce->required)
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
g_free(pce);
}
g_list_free(parts);
purple_request_fields(NULL, _("Edit Chat"), NULL, _("Please Update the necessary fields."),
fields, _("Edit"), G_CALLBACK(chat_components_edit_ok), _("Cancel"), NULL,
NULL, chat);
}
static void
autojoin_toggled(GntMenuItem *item, gpointer data)
{
PurpleActionMenu *action = data;
purple_blist_node_set_bool(purple_action_menu_get_data(action), "gnt-autojoin",
gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item)));
}
static void
create_chat_menu(GntMenu *menu, PurpleChat *chat)
{
PurpleActionMenu *action = purple_action_menu_new(_("Auto-join"), NULL, chat, NULL);
GntMenuItem *check = gnt_menuitem_check_new(
purple_action_menu_get_label(action));
gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(check),
purple_blist_node_get_bool((PurpleBlistNode*)chat, "gnt-autojoin"));
gnt_menu_add_item(menu, check);
gnt_menuitem_set_callback(check, autojoin_toggled, action);
g_signal_connect_swapped(G_OBJECT(menu), "destroy",
G_CALLBACK(purple_action_menu_free), action);
/* Protocol actions */
append_proto_menu(menu,
purple_account_get_connection(purple_chat_get_account(chat)),
(PurpleBlistNode*)chat);
add_custom_action(menu, _("Edit Settings"), (PurpleCallback)chat_components_edit, chat);
}
static void
finch_add_buddy(PurpleBlistNode *selected, PurpleGroup *grp)
{
purple_blist_request_add_buddy(NULL, NULL, grp ? purple_group_get_name(grp) : NULL, NULL);
}
static void
finch_add_group(PurpleBlistNode *selected, PurpleGroup *grp)
{
purple_blist_request_add_group();
}
static void
finch_add_chat(PurpleBlistNode *selected, PurpleGroup *grp)
{
purple_blist_request_add_chat(NULL, grp, NULL, NULL);
}
static void
create_group_menu(GntMenu *menu, PurpleGroup *group)
{
add_custom_action(menu, _("Add Buddy"),
PURPLE_CALLBACK(finch_add_buddy), group);
add_custom_action(menu, _("Add Chat"),
PURPLE_CALLBACK(finch_add_chat), group);
add_custom_action(menu, _("Add Group"),
PURPLE_CALLBACK(finch_add_group), group);
}
gpointer finch_retrieve_user_info(PurpleConnection *conn, const char *name)
{
PurpleNotifyUserInfo *info = purple_notify_user_info_new();
gpointer uihandle;
purple_notify_user_info_add_pair_plaintext(info, _("Information"), _("Retrieving..."));
uihandle = purple_notify_userinfo(conn, name, info, NULL, NULL);
purple_notify_user_info_destroy(info);
purple_serv_get_info(conn, name);
return uihandle;
}
static void
finch_blist_get_buddy_info_cb(PurpleBlistNode *selected, PurpleBuddy *buddy)
{
finch_retrieve_user_info(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy));
}
static void
finch_blist_menu_send_file_cb(PurpleBlistNode *selected, PurpleBuddy *buddy)
{
purple_serv_send_file(purple_account_get_connection(purple_buddy_get_account(buddy)), purple_buddy_get_name(buddy), NULL);
}
static void
toggle_block_buddy(GntMenuItem *item, gpointer buddy)
{
gboolean block = gnt_menuitem_check_get_checked(GNT_MENU_ITEM_CHECK(item));
PurpleAccount *account = purple_buddy_get_account(buddy);
const char *name = purple_buddy_get_name(buddy);
block ? purple_account_privacy_deny(account, name) :
purple_account_privacy_allow(account, name);
}
static void
toggle_show_offline(GntMenuItem *item, gpointer buddy)
{
purple_blist_node_set_bool(buddy, "show_offline",
!purple_blist_node_get_bool(buddy, "show_offline"));
if (!ggblist->manager->can_add_node(buddy))
node_remove(purple_blist_get_default(), buddy);
else
node_update(purple_blist_get_default(), buddy);
}
static void
create_buddy_menu(GntMenu *menu, PurpleBuddy *buddy)
{
PurpleAccount *account;
gboolean permitted;
GntMenuItem *item;
PurpleProtocol *protocol;
PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
protocol = purple_connection_get_protocol(gc);
if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_info))
{
add_custom_action(menu, _("Get Info"),
PURPLE_CALLBACK(finch_blist_get_buddy_info_cb), buddy);
}
if (PURPLE_IS_PROTOCOL_XFER(protocol))
{
if (purple_protocol_xfer_can_receive(
PURPLE_PROTOCOL_XFER(protocol),
gc,
purple_buddy_get_name(buddy))
) {
add_custom_action(menu, _("Send File"),
PURPLE_CALLBACK(finch_blist_menu_send_file_cb), buddy);
}
}
account = purple_buddy_get_account(buddy);
permitted = purple_account_privacy_check(account, purple_buddy_get_name(buddy));
item = gnt_menuitem_check_new(_("Blocked"));
gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item), !permitted);
gnt_menuitem_set_callback(item, toggle_block_buddy, buddy);
gnt_menu_add_item(menu, item);
item = gnt_menuitem_check_new(_("Show when offline"));
gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item), purple_blist_node_get_bool((PurpleBlistNode*)buddy, "show_offline"));
gnt_menuitem_set_callback(item, toggle_show_offline, buddy);
gnt_menu_add_item(menu, item);
/* Protocol actions */
append_proto_menu(menu,
purple_account_get_connection(purple_buddy_get_account(buddy)),
(PurpleBlistNode*)buddy);
}
static void
append_extended_menu(GntMenu *menu, PurpleBlistNode *node)
{
GList *iter;
for (iter = purple_blist_node_get_extended_menu(node);
iter; iter = g_list_delete_link(iter, iter))
{
finch_append_menu_action(menu, iter->data, node);
}
}
/* Xerox'd from gtkdialogs.c:purple_gtkdialogs_remove_contact_cb */
static void
remove_contact(PurpleContact *contact)
{
PurpleBlistNode *bnode, *cnode;
PurpleGroup *group;
cnode = (PurpleBlistNode *)contact;
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;
PurpleAccount *account = purple_buddy_get_account(buddy);
if (purple_account_is_connected(account))
purple_account_remove_buddy(account, buddy, group);
}
purple_blist_remove_contact(contact);
}
static void
rename_blist_node(PurpleBlistNode *node, const char *newname)
{
const char *name = newname;
if (name && !*name)
name = NULL;
if (PURPLE_IS_CONTACT(node)) {
PurpleContact *contact = (PurpleContact*)node;
PurpleBuddy *buddy = purple_contact_get_priority_buddy(contact);
purple_contact_set_alias(contact, name);
purple_buddy_set_local_alias(buddy, name);
purple_serv_alias_buddy(buddy);
} else if (PURPLE_IS_BUDDY(node)) {
purple_buddy_set_local_alias((PurpleBuddy*)node, name);
purple_serv_alias_buddy((PurpleBuddy*)node);
} else if (PURPLE_IS_CHAT(node))
purple_chat_set_alias((PurpleChat*)node, name);
else if (PURPLE_IS_GROUP(node) && (name != NULL))
purple_group_set_name((PurpleGroup*)node, name);
else
g_return_if_reached();
}
static void
finch_blist_rename_node_cb(PurpleBlistNode *selected, PurpleBlistNode *node)
{
const char *name = NULL;
char *prompt;
const char *text;
if (PURPLE_IS_CONTACT(node))
name = purple_contact_get_alias((PurpleContact*)node);
else if (PURPLE_IS_BUDDY(node))
name = purple_buddy_get_contact_alias((PurpleBuddy*)node);
else if (PURPLE_IS_CHAT(node))
name = purple_chat_get_name((PurpleChat*)node);
else if (PURPLE_IS_GROUP(node))
name = purple_group_get_name((PurpleGroup*)node);
else
g_return_if_reached();
prompt = g_strdup_printf(_("Please enter the new name for %s"), name);
text = PURPLE_IS_GROUP(node) ? _("Rename") : _("Set Alias");
purple_request_input(node, text, prompt, _("Enter empty string to reset the name."),
name, FALSE, FALSE, NULL, text, G_CALLBACK(rename_blist_node),
_("Cancel"), NULL,
NULL, node);
g_free(prompt);
}
static void showlog_cb(PurpleBlistNode *sel, PurpleBlistNode *node)
{
PurpleLogType type;
PurpleAccount *account;
char *name = NULL;
if (PURPLE_IS_BUDDY(node)) {
PurpleBuddy *b = (PurpleBuddy*) node;
type = PURPLE_LOG_IM;
name = g_strdup(purple_buddy_get_name(b));
account = purple_buddy_get_account(b);
} else if (PURPLE_IS_CHAT(node)) {
PurpleChat *c = (PurpleChat*) node;
PurpleProtocol *protocol = NULL;
type = PURPLE_LOG_CHAT;
account = purple_chat_get_account(c);
protocol = purple_account_get_protocol(account);
if (protocol) {
name = purple_protocol_chat_get_name(PURPLE_PROTOCOL_CHAT(protocol), purple_chat_get_components(c));
}
} else if (PURPLE_IS_CONTACT(node)) {
finch_log_show_contact((PurpleContact *)node);
return;
} else {
/* This callback should not have been registered for a node
* that doesn't match the type of one of the blocks above. */
g_return_if_reached();
}
if (name && account) {
finch_log_show(type, name, account);
g_free(name);
}
}
/* Xeroxed from gtkdialogs.c:purple_gtkdialogs_remove_group_cb*/
static void
remove_group(PurpleGroup *group)
{
PurpleBlistNode *cnode, *bnode;
cnode = purple_blist_node_get_first_child(((PurpleBlistNode*)group));
while (cnode) {
if (PURPLE_IS_CONTACT(cnode)) {
bnode = purple_blist_node_get_first_child(cnode);
cnode = purple_blist_node_get_sibling_next(cnode);
while (bnode) {
PurpleBuddy *buddy;
if (PURPLE_IS_BUDDY(bnode)) {
PurpleAccount *account;
buddy = (PurpleBuddy*)bnode;
bnode = purple_blist_node_get_sibling_next(bnode);
account = purple_buddy_get_account(buddy);
if (purple_account_is_connected(account)) {
purple_account_remove_buddy(account, buddy, group);
purple_blist_remove_buddy(buddy);
}
} else {
bnode = purple_blist_node_get_sibling_next(bnode);
}
}
} else if (PURPLE_IS_CHAT(cnode)) {
PurpleChat *chat = (PurpleChat *)cnode;
cnode = purple_blist_node_get_sibling_next(cnode);
if (purple_account_is_connected(purple_chat_get_account(chat)))
purple_blist_remove_chat(chat);
} else {
cnode = purple_blist_node_get_sibling_next(cnode);
}
}
purple_blist_remove_group(group);
}
static void
finch_blist_remove_node(PurpleBlistNode *node)
{
if (PURPLE_IS_CONTACT(node)) {
remove_contact((PurpleContact*)node);
} else if (PURPLE_IS_BUDDY(node)) {
PurpleBuddy *buddy = (PurpleBuddy*)node;
PurpleGroup *group = purple_buddy_get_group(buddy);
purple_account_remove_buddy(purple_buddy_get_account(buddy), buddy, group);
purple_blist_remove_buddy(buddy);
} else if (PURPLE_IS_CHAT(node)) {
purple_blist_remove_chat((PurpleChat*)node);
} else if (PURPLE_IS_GROUP(node)) {
remove_group((PurpleGroup*)node);
}
}
static void
finch_blist_remove_node_cb(PurpleBlistNode *selected, PurpleBlistNode *node)
{
PurpleAccount *account = NULL;
char *primary;
const char *name, *sec = NULL;
if (PURPLE_IS_CONTACT(node)) {
PurpleContact *c = (PurpleContact*)node;
name = purple_contact_get_alias(c);
if (purple_counting_node_get_total_size(PURPLE_COUNTING_NODE(c)) > 1)
sec = _("Removing this contact will also remove all the buddies in the contact");
} else if (PURPLE_IS_BUDDY(node)) {
name = purple_buddy_get_name((PurpleBuddy*)node);
account = purple_buddy_get_account((PurpleBuddy*)node);
} else if (PURPLE_IS_CHAT(node)) {
name = purple_chat_get_name((PurpleChat*)node);
} else if (PURPLE_IS_GROUP(node)) {
name = purple_group_get_name((PurpleGroup*)node);
sec = _("Removing this group will also remove all the buddies in the group");
}
else
return;
primary = g_strdup_printf(_("Are you sure you want to remove %s?"), name);
/* XXX: anything to do with the returned ui-handle? */
purple_request_action(node, _("Confirm Remove"),
primary, sec,
1,
purple_request_cpar_from_account(account),
node, 2,
_("Remove"), finch_blist_remove_node,
_("Cancel"), NULL);
g_free(primary);
}
static void
finch_blist_toggle_tag_buddy(PurpleBlistNode *node)
{
GList *iter;
if (node == NULL)
return;
if (ggblist->tagged && (iter = g_list_find(ggblist->tagged, node)) != NULL) {
ggblist->tagged = g_list_delete_link(ggblist->tagged, iter);
} else {
ggblist->tagged = g_list_prepend(ggblist->tagged, node);
}
if (PURPLE_IS_CONTACT(node))
update_buddy_display(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)), ggblist);
else if (PURPLE_IS_BUDDY(node))
update_buddy_display((PurpleBuddy*)node, ggblist);
else
update_node_display(node, ggblist);
}
static void
finch_blist_place_tagged(PurpleBlistNode *target)
{
PurpleGroup *tg = NULL;
PurpleContact *tc = NULL;
if (PURPLE_IS_GROUP(target))
tg = (PurpleGroup*)target;
else if (PURPLE_IS_BUDDY(target)) {
tc = (PurpleContact*)purple_blist_node_get_parent(target);
tg = (PurpleGroup*)purple_blist_node_get_parent((PurpleBlistNode*)tc);
} else if (PURPLE_IS_CONTACT(target)) {
tc = (PurpleContact *)target;
tg = (PurpleGroup *)purple_blist_node_get_parent(target);
} else if (PURPLE_IS_CHAT(target)) {
tg = (PurpleGroup*)purple_blist_node_get_parent(target);
} else {
return;
}
if (ggblist->tagged) {
GList *list = ggblist->tagged;
ggblist->tagged = NULL;
while (list) {
PurpleBlistNode *node = list->data;
list = g_list_delete_link(list, list);
if (PURPLE_IS_GROUP(node)) {
update_node_display(node, ggblist);
/* Add the group after the current group */
purple_blist_add_group((PurpleGroup*)node, (PurpleBlistNode*)tg);
} else if (PURPLE_IS_CONTACT(node)) {
update_buddy_display(purple_contact_get_priority_buddy((PurpleContact*)node), ggblist);
if (PURPLE_BLIST_NODE(tg) == target) {
/* The target is a group, just add the contact to the group. */
purple_blist_add_contact((PurpleContact*)node, tg, NULL);
} else if (tc) {
/* The target is either a buddy, or a contact. Merge with that contact. */
purple_contact_merge((PurpleContact*)node, (PurpleBlistNode*)tc);
} else {
/* The target is a chat. Add the contact to the group after this chat. */
purple_blist_add_contact((PurpleContact*)node, NULL, target);
}
} else if (PURPLE_IS_BUDDY(node)) {
update_buddy_display((PurpleBuddy*)node, ggblist);
if (PURPLE_BLIST_NODE(tg) == target) {
/* The target is a group. Add this buddy in a new contact under this group. */
purple_blist_add_buddy((PurpleBuddy*)node, NULL, tg, NULL);
} else if (PURPLE_IS_CONTACT(target)) {
/* Add to the contact. */
purple_blist_add_buddy((PurpleBuddy*)node, tc, NULL, NULL);
} else if (PURPLE_IS_BUDDY(target)) {
/* Add to the contact after the selected buddy. */
purple_blist_add_buddy((PurpleBuddy*)node, NULL, NULL, target);
} else if (PURPLE_IS_CHAT(target)) {
/* Add to the selected chat's group. */
purple_blist_add_buddy((PurpleBuddy*)node, NULL, tg, NULL);
}
} else if (PURPLE_IS_CHAT(node)) {
update_node_display(node, ggblist);
if (PURPLE_BLIST_NODE(tg) == target)
purple_blist_add_chat((PurpleChat*)node, tg, NULL);
else
purple_blist_add_chat((PurpleChat*)node, NULL, target);
}
}
}
}
static void
context_menu_destroyed(GntWidget *widget, FinchBuddyList *ggblist)
{
ggblist->context = NULL;
}
static void
draw_context_menu(FinchBuddyList *ggblist)
{
PurpleBlistNode *node = NULL;
GntWidget *context = NULL;
GntTree *tree = NULL;
int x, y, top, width;
char *title = NULL;
if (ggblist->context)
return;
tree = GNT_TREE(ggblist->tree);
node = gnt_tree_get_selection_data(tree);
if (node && !(PURPLE_IS_BUDDY(node) || PURPLE_IS_CONTACT(node) ||
PURPLE_IS_GROUP(node) || PURPLE_IS_CHAT(node)))
return;
if (ggblist->tooltip)
remove_tooltip(ggblist);
ggblist->cnode = node;
ggblist->context = context = gnt_menu_new(GNT_MENU_POPUP);
g_signal_connect(G_OBJECT(context), "destroy", G_CALLBACK(context_menu_destroyed), ggblist);
g_signal_connect(G_OBJECT(context), "hide", G_CALLBACK(gnt_widget_destroy), NULL);
if (!node) {
create_group_menu(GNT_MENU(context), NULL);
title = g_strdup(_("Buddy List"));
} else if (PURPLE_IS_CONTACT(node)) {
ggblist->cnode = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(node)));
create_buddy_menu(GNT_MENU(context), (PurpleBuddy*)ggblist->cnode);
title = g_strdup(purple_contact_get_alias((PurpleContact*)node));
} else if (PURPLE_IS_BUDDY(node)) {
PurpleBuddy *buddy = (PurpleBuddy *)node;
create_buddy_menu(GNT_MENU(context), buddy);
title = g_strdup(purple_buddy_get_name(buddy));
} else if (PURPLE_IS_CHAT(node)) {
PurpleChat *chat = (PurpleChat*)node;
create_chat_menu(GNT_MENU(context), chat);
title = g_strdup(purple_chat_get_name(chat));
} else if (PURPLE_IS_GROUP(node)) {
PurpleGroup *group = (PurpleGroup *)node;
create_group_menu(GNT_MENU(context), group);
title = g_strdup(purple_group_get_name(group));
}
append_extended_menu(GNT_MENU(context), node);
/* These are common for everything */
if (node) {
add_custom_action(GNT_MENU(context),
PURPLE_IS_GROUP(node) ? _("Rename") : _("Alias"),
PURPLE_CALLBACK(finch_blist_rename_node_cb), node);
add_custom_action(GNT_MENU(context), _("Remove"),
PURPLE_CALLBACK(finch_blist_remove_node_cb), node);
if (ggblist->tagged && (PURPLE_IS_CONTACT(node)
|| PURPLE_IS_GROUP(node))) {
add_custom_action(GNT_MENU(context), _("Place tagged"),
PURPLE_CALLBACK(finch_blist_place_tagged), node);
}
if (PURPLE_IS_BUDDY(node) || PURPLE_IS_CONTACT(node)) {
add_custom_action(GNT_MENU(context), _("Toggle Tag"),
PURPLE_CALLBACK(finch_blist_toggle_tag_buddy), node);
}
if (!PURPLE_IS_GROUP(node)) {
add_custom_action(GNT_MENU(context), _("View Log"),
PURPLE_CALLBACK(showlog_cb), node);
}
}
/* Set the position for the popup */
gnt_widget_get_position(GNT_WIDGET(tree), &x, &y);
gnt_widget_get_size(GNT_WIDGET(tree), &width, NULL);
top = gnt_tree_get_selection_visible_line(tree);
x += width;
y += top - 1;
gnt_widget_set_position(context, x, y);
gnt_screen_menu_show(GNT_MENU(context));
g_free(title);
}
static void
tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full)
{
PurpleProtocol *protocol;
PurpleAccount *account;
PurpleNotifyUserInfo *user_info;
PurplePresence *presence;
const char *alias = purple_buddy_get_alias(buddy);
char *tmp, *strip;
user_info = purple_notify_user_info_new();
account = purple_buddy_get_account(buddy);
presence = purple_buddy_get_presence(buddy);
if (!full || g_utf8_collate(purple_buddy_get_name(buddy), alias)) {
purple_notify_user_info_add_pair_plaintext(user_info, _("Nickname"), alias);
}
tmp = g_strdup_printf("%s (%s)",
purple_account_get_username(account),
purple_account_get_protocol_name(account));
purple_notify_user_info_add_pair_plaintext(user_info, _("Account"), tmp);
g_free(tmp);
protocol = purple_account_get_protocol(account);
if (protocol) {
purple_protocol_client_tooltip_text(PURPLE_PROTOCOL_CLIENT(protocol), buddy, user_info, full);
}
if (purple_prefs_get_bool("/finch/blist/idletime")) {
PurplePresence *pre = purple_buddy_get_presence(buddy);
if (purple_presence_is_idle(pre)) {
time_t idle = purple_presence_get_idle_time(pre);
if (idle > 0) {
char *st = purple_str_seconds_to_string(time(NULL) - idle);
purple_notify_user_info_add_pair_plaintext(user_info, _("Idle"), st);
g_free(st);
}
}
}
tmp = purple_notify_user_info_get_text_with_newline(user_info, "<BR>");
purple_notify_user_info_destroy(user_info);
strip = purple_markup_strip_html(tmp);
g_string_append(str, strip);
if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOBILE)) {
g_string_append(str, "\n");
g_string_append(str, _("On Mobile"));
}
g_free(strip);
g_free(tmp);
}
static GString*
make_sure_text_fits(GString *string)
{
int maxw = getmaxx(stdscr) - 3;
char *str = gnt_util_onscreen_fit_string(string->str, maxw);
string = g_string_assign(string, str);
g_free(str);
return string;
}
static gboolean
draw_tooltip_real(FinchBuddyList *ggblist)
{
PurpleBlistNode *node;
int x, y, top, width, w, h;
GString *str = NULL;
GntTree *tree;
GntWidget *widget, *box, *tv;
char *title = NULL;
widget = ggblist->tree;
tree = GNT_TREE(widget);
if (!gnt_widget_has_focus(ggblist->tree) ||
(ggblist->context && gnt_widget_get_visible(ggblist->context)))
return FALSE;
if (ggblist->tooltip)
{
/* XXX: Once we can properly redraw on expose events, this can be removed at the end
* to avoid the blinking*/
remove_tooltip(ggblist);
}
node = gnt_tree_get_selection_data(tree);
if (!node)
return FALSE;
if (!ggblist->manager->create_tooltip(node, &str, &title))
return FALSE;
gnt_widget_get_position(widget, &x, &y);
gnt_widget_get_size(widget, &width, NULL);
top = gnt_tree_get_selection_visible_line(tree);
x += width;
y += top - 1;
box = gnt_box_new(FALSE, FALSE);
gnt_box_set_toplevel(GNT_BOX(box), TRUE);
gnt_widget_set_has_shadow(box, FALSE);
gnt_box_set_title(GNT_BOX(box), title);
str = make_sure_text_fits(str);
gnt_util_get_text_bound(str->str, &w, &h);
h = MAX(1, h);
tv = gnt_text_view_new();
gnt_widget_set_size(tv, w + 1, h);
gnt_text_view_set_flag(GNT_TEXT_VIEW(tv), GNT_TEXT_VIEW_NO_SCROLL);
gnt_box_add_widget(GNT_BOX(box), tv);
if (x + w >= getmaxx(stdscr))
x -= w + width + 2;
gnt_widget_set_position(box, x, y);
gnt_widget_set_take_focus(box, FALSE);
gnt_widget_set_transient(box, TRUE);
gnt_widget_draw(box);
gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(tv), str->str, GNT_TEXT_FLAG_NORMAL);
gnt_text_view_scroll(GNT_TEXT_VIEW(tv), 0);
g_free(title);
g_string_free(str, TRUE);
ggblist->tooltip = box;
ggblist->tnode = node;
gnt_widget_set_name(ggblist->tooltip, "tooltip");
return FALSE;
}
static void
draw_tooltip(FinchBuddyList *ggblist)
{
/* When an account has signed off, it removes one buddy at a time.
* Drawing the tooltip after removing each buddy is expensive. On
* top of that, if the selected buddy belongs to the disconnected
* account, then retreiving the tooltip for that causes crash. So
* let's make sure we wait for all the buddies to be removed first.*/
int id = g_timeout_add(0, (GSourceFunc)draw_tooltip_real, ggblist);
g_object_set_data_full(G_OBJECT(ggblist->window), "draw_tooltip_calback",
GINT_TO_POINTER(id), (GDestroyNotify)g_source_remove);
}
static void
selection_changed(GntWidget *widget, gpointer old, gpointer current,
FinchBuddyList *ggblist)
{
remove_peripherals(ggblist);
draw_tooltip(ggblist);
}
static gboolean
context_menu(GntWidget *widget, FinchBuddyList *ggblist)
{
draw_context_menu(ggblist);
return TRUE;
}
static gboolean
key_pressed(GntWidget *widget, const char *text, FinchBuddyList *ggblist)
{
if (text[0] == 27 && text[1] == 0) {
/* Escape was pressed */
if (gnt_tree_is_searching(GNT_TREE(ggblist->tree)))
gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist->tree), "end-search", NULL);
remove_peripherals(ggblist);
} else if (purple_strequal(text, GNT_KEY_INS)) {
PurpleBlistNode *node = gnt_tree_get_selection_data(GNT_TREE(ggblist->tree));
purple_blist_request_add_buddy(NULL, NULL,
node && PURPLE_IS_GROUP(node) ? purple_group_get_name(PURPLE_GROUP(node)) : NULL,
NULL);
} else if (!gnt_tree_is_searching(GNT_TREE(ggblist->tree))) {
if (purple_strequal(text, "t")) {
finch_blist_toggle_tag_buddy(gnt_tree_get_selection_data(GNT_TREE(ggblist->tree)));
gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist->tree), "move-down", NULL);
} else if (purple_strequal(text, "a")) {
finch_blist_place_tagged(gnt_tree_get_selection_data(GNT_TREE(ggblist->tree)));
} else
return FALSE;
} else
return FALSE;
return TRUE;
}
static void
update_node_display(PurpleBlistNode *node, FinchBuddyList *ggblist)
{
GntTextFormatFlags flag = get_blist_node_flag(ggblist, node);
gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node, flag);
}
static void
update_buddy_display(PurpleBuddy *buddy, FinchBuddyList *ggblist)
{
PurpleContact *contact;
contact = purple_buddy_get_contact(buddy);
gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, 0, get_display_name((PurpleBlistNode*)buddy));
gnt_tree_change_text(GNT_TREE(ggblist->tree), contact, 0, get_display_name((PurpleBlistNode*)contact));
blist_update_row_flags(ggblist, (PurpleBlistNode *)buddy);
if (buddy == purple_contact_get_priority_buddy(contact))
blist_update_row_flags(ggblist, (PurpleBlistNode *)contact);
if (ggblist->tnode == (PurpleBlistNode *)buddy) {
draw_tooltip(ggblist);
}
}
static void
buddy_status_changed(PurpleBuddy *buddy, PurpleStatus *old, PurpleStatus *now,
FinchBuddyList *ggblist)
{
update_buddy_display(buddy, ggblist);
}
static void
buddy_idle_changed(PurpleBuddy *buddy, int old, int new,
FinchBuddyList *ggblist)
{
update_buddy_display(buddy, ggblist);
}
static void
remove_peripherals(FinchBuddyList *ggblist)
{
if (ggblist->tooltip)
remove_tooltip(ggblist);
else if (ggblist->context)
gnt_widget_destroy(ggblist->context);
}
static void
size_changed_cb(GntWidget *w, int wi, int h)
{
int width, height;
gnt_widget_get_size(w, &width, &height);
purple_prefs_set_int(PREF_ROOT "/size/width", width);
purple_prefs_set_int(PREF_ROOT "/size/height", height);
}
static void
save_position_cb(GntWidget *w, int x, int y)
{
purple_prefs_set_int(PREF_ROOT "/position/x", x);
purple_prefs_set_int(PREF_ROOT "/position/y", y);
}
static void
reset_blist_window(GntWidget *window, gpointer null)
{
purple_signals_disconnect_by_handle(finch_blist_get_handle());
if (ggblist->typing)
g_source_remove(ggblist->typing);
remove_peripherals(ggblist);
if (ggblist->tagged)
g_list_free(ggblist->tagged);
if (ggblist->new_group_timeout)
g_source_remove(ggblist->new_group_timeout);
if (ggblist->new_group)
g_list_free(ggblist->new_group);
ggblist = NULL;
}
static void
populate_buddylist(void)
{
PurpleBlistNode *node;
PurpleBuddyList *list;
if (ggblist->manager->init)
ggblist->manager->init();
if (purple_strequal(purple_prefs_get_string(PREF_ROOT "/sort_type"), "text")) {
gnt_tree_set_compare_func(GNT_TREE(ggblist->tree),
(GCompareFunc)blist_node_compare_text);
} else if (purple_strequal(purple_prefs_get_string(PREF_ROOT "/sort_type"), "status")) {
gnt_tree_set_compare_func(GNT_TREE(ggblist->tree),
(GCompareFunc)blist_node_compare_status);
} else if (purple_strequal(purple_prefs_get_string(PREF_ROOT "/sort_type"), "log")) {
gnt_tree_set_compare_func(GNT_TREE(ggblist->tree),
(GCompareFunc)blist_node_compare_log);
}
list = purple_blist_get_default();
node = purple_blist_get_root(list);
while (node)
{
node_update(list, node);
node = purple_blist_node_next(node, FALSE);
}
}
static void
destroy_status_list(GList *list)
{
g_list_free_full(list, g_free);
}
static void
populate_status_dropdown(void)
{
int i;
GList *iter;
GList *items = NULL;
StatusBoxItem *item = NULL;
/* First the primitives */
PurpleStatusPrimitive prims[] = {PURPLE_STATUS_AVAILABLE, PURPLE_STATUS_AWAY,
PURPLE_STATUS_INVISIBLE, PURPLE_STATUS_OFFLINE, PURPLE_STATUS_UNSET};
gnt_combo_box_remove_all(GNT_COMBO_BOX(ggblist->status));
for (i = 0; prims[i] != PURPLE_STATUS_UNSET; i++)
{
item = g_new0(StatusBoxItem, 1);
item->type = STATUS_PRIMITIVE;
item->u.prim = prims[i];
items = g_list_prepend(items, item);
gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
purple_primitive_get_name_from_type(prims[i]));
}
/* Now the popular statuses */
for (iter = purple_savedstatuses_get_popular(6); iter; iter = g_list_delete_link(iter, iter))
{
item = g_new0(StatusBoxItem, 1);
item->type = STATUS_SAVED_POPULAR;
item->u.saved = iter->data;
items = g_list_prepend(items, item);
gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
purple_savedstatus_get_title(iter->data));
}
/* New savedstatus */
item = g_new0(StatusBoxItem, 1);
item->type = STATUS_SAVED_NEW;
items = g_list_prepend(items, item);
gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
_("New..."));
/* More savedstatuses */
item = g_new0(StatusBoxItem, 1);
item->type = STATUS_SAVED_ALL;
items = g_list_prepend(items, item);
gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item,
_("Saved..."));
/* The keys for the combobox are created here, and never used
* anywhere else. So make sure the keys are freed when the widget
* is destroyed. */
g_object_set_data_full(G_OBJECT(ggblist->status), "list of statuses",
items, (GDestroyNotify)destroy_status_list);
}
static void
redraw_blist(const char *name, PurplePrefType type, gconstpointer val, gpointer data)
{
PurpleBlistNode *sel;
FinchBlistManager *manager;
if (ggblist == NULL)
return;
manager = finch_blist_manager_find(purple_prefs_get_string(PREF_ROOT "/grouping"));
if (manager == NULL)
manager = &default_manager;
if (ggblist->manager != manager) {
if (ggblist->manager->uninit)
ggblist->manager->uninit();
ggblist->manager = manager;
if (manager->can_add_node == NULL)
manager->can_add_node = default_can_add_node;
if (manager->find_parent == NULL)
manager->find_parent = default_find_parent;
if (manager->create_tooltip == NULL)
manager->create_tooltip = default_create_tooltip;
}
if (ggblist->window == NULL)
return;
sel = gnt_tree_get_selection_data(GNT_TREE(ggblist->tree));
gnt_tree_remove_all(GNT_TREE(ggblist->tree));
populate_buddylist();
gnt_tree_set_selected(GNT_TREE(ggblist->tree), sel);
draw_tooltip(ggblist);
}
void finch_blist_init()
{
color_available = gnt_style_get_color(NULL, "color-available");
if (!color_available)
color_available = gnt_color_add_pair(COLOR_GREEN, -1);
color_away = gnt_style_get_color(NULL, "color-away");
if (!color_away)
color_away = gnt_color_add_pair(COLOR_BLUE, -1);
color_idle = gnt_style_get_color(NULL, "color-idle");
if (!color_idle)
color_idle = gnt_color_add_pair(COLOR_CYAN, -1);
color_offline = gnt_style_get_color(NULL, "color-offline");
if (!color_offline)
color_offline = gnt_color_add_pair(COLOR_RED, -1);
purple_prefs_add_none(PREF_ROOT);
purple_prefs_add_none(PREF_ROOT "/size");
purple_prefs_add_int(PREF_ROOT "/size/width", 20);
purple_prefs_add_int(PREF_ROOT "/size/height", 17);
purple_prefs_add_none(PREF_ROOT "/position");
purple_prefs_add_int(PREF_ROOT "/position/x", 0);
purple_prefs_add_int(PREF_ROOT "/position/y", 0);
purple_prefs_add_bool(PREF_ROOT "/idletime", TRUE);
purple_prefs_add_bool(PREF_ROOT "/showoffline", FALSE);
purple_prefs_add_bool(PREF_ROOT "/emptygroups", FALSE);
purple_prefs_add_string(PREF_ROOT "/sort_type", "text");
purple_prefs_add_string(PREF_ROOT "/grouping", "default");
purple_prefs_connect_callback(finch_blist_get_handle(),
PREF_ROOT "/emptygroups", redraw_blist, NULL);
purple_prefs_connect_callback(finch_blist_get_handle(),
PREF_ROOT "/showoffline", redraw_blist, NULL);
purple_prefs_connect_callback(finch_blist_get_handle(),
PREF_ROOT "/sort_type", redraw_blist, NULL);
purple_prefs_connect_callback(finch_blist_get_handle(),
PREF_ROOT "/grouping", redraw_blist, NULL);
purple_signal_connect_priority(purple_connections_get_handle(),
"autojoin", purple_blist_get_handle(),
G_CALLBACK(account_autojoin_cb), NULL,
PURPLE_SIGNAL_PRIORITY_HIGHEST);
finch_blist_install_manager(&default_manager);
}
static gboolean
remove_typing_cb(gpointer null)
{
PurpleSavedStatus *current;
const char *message, *newmessage;
char *escnewmessage;
PurpleStatusPrimitive prim, newprim;
StatusBoxItem *item;
current = purple_savedstatus_get_current();
message = purple_savedstatus_get_message(current);
prim = purple_savedstatus_get_primitive_type(current);
newmessage = gnt_entry_get_text(GNT_ENTRY(ggblist->statustext));
item = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(ggblist->status));
escnewmessage = newmessage ? g_markup_escape_text(newmessage, -1) : NULL;
switch (item->type) {
case STATUS_PRIMITIVE:
newprim = item->u.prim;
break;
case STATUS_SAVED_POPULAR:
newprim = purple_savedstatus_get_primitive_type(item->u.saved);
break;
default:
goto end; /* 'New' or 'Saved' is selected, but this should never happen. */
}
if (newprim != prim || ((message && !escnewmessage) ||
(!message && escnewmessage) ||
(message && escnewmessage && g_utf8_collate(message, escnewmessage) != 0)))
{
PurpleSavedStatus *status = purple_savedstatus_find_transient_by_type_and_message(newprim, escnewmessage);
/* Holy Crap! That's a LAWNG function name */
if (status == NULL)
{
status = purple_savedstatus_new(NULL, newprim);
purple_savedstatus_set_message(status, escnewmessage);
}
purple_savedstatus_activate(status);
}
gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree);
end:
g_free(escnewmessage);
if (ggblist->typing)
g_source_remove(ggblist->typing);
ggblist->typing = 0;
return FALSE;
}
static void
status_selection_changed(GntComboBox *box, StatusBoxItem *old, StatusBoxItem *now, gpointer null)
{
gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), NULL);
if (now->type == STATUS_SAVED_POPULAR)
{
/* Set the status immediately */
purple_savedstatus_activate(now->u.saved);
}
else if (now->type == STATUS_PRIMITIVE)
{
/* Move the focus to the entry box */
/* XXX: Make sure the selected status can have a message */
gnt_box_move_focus(GNT_BOX(ggblist->window), 1);
ggblist->typing = g_timeout_add_seconds(TYPING_TIMEOUT_S, (GSourceFunc)remove_typing_cb, NULL);
}
else if (now->type == STATUS_SAVED_ALL)
{
/* Restore the selection to reflect current status. */
savedstatus_changed(purple_savedstatus_get_current(), NULL);
gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree);
finch_savedstatus_show_all();
}
else if (now->type == STATUS_SAVED_NEW)
{
savedstatus_changed(purple_savedstatus_get_current(), NULL);
gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree);
finch_savedstatus_edit(NULL);
}
else
g_return_if_reached();
}
static gboolean
status_text_changed(GntEntry *entry, const char *text, gpointer null)
{
if ((text[0] == 27 || (text[0] == '\t' && text[1] == '\0')) && ggblist->typing == 0)
return FALSE;
if (ggblist->typing)
g_source_remove(ggblist->typing);
ggblist->typing = 0;
if (text[0] == '\r' && text[1] == 0)
{
/* Set the status only after you press 'Enter' */
remove_typing_cb(NULL);
return TRUE;
}
ggblist->typing = g_timeout_add_seconds(TYPING_TIMEOUT_S, (GSourceFunc)remove_typing_cb, NULL);
return FALSE;
}
static void
savedstatus_changed(PurpleSavedStatus *now, PurpleSavedStatus *old)
{
GList *list;
PurpleStatusPrimitive prim;
const char *message;
gboolean found = FALSE, saved = TRUE;
if (!ggblist)
return;
/* Block the signals we don't want to emit */
g_signal_handlers_block_matched(ggblist->status, G_SIGNAL_MATCH_FUNC,
0, 0, NULL, status_selection_changed, NULL);
g_signal_handlers_block_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC,
0, 0, NULL, status_text_changed, NULL);
prim = purple_savedstatus_get_primitive_type(now);
message = purple_savedstatus_get_message(now);
/* Rebuild the status dropdown */
populate_status_dropdown();
while (!found) {
list = g_object_get_data(G_OBJECT(ggblist->status), "list of statuses");
for (; list; list = list->next)
{
StatusBoxItem *item = list->data;
if ((saved && item->type != STATUS_PRIMITIVE && item->u.saved == now) ||
(!saved && item->type == STATUS_PRIMITIVE && item->u.prim == prim))
{
char *mess = purple_unescape_html(message);
gnt_combo_box_set_selected(GNT_COMBO_BOX(ggblist->status), item);
gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), mess);
gnt_widget_draw(ggblist->status);
g_free(mess);
found = TRUE;
break;
}
}
if (!saved)
break;
saved = FALSE;
}
g_signal_handlers_unblock_matched(ggblist->status, G_SIGNAL_MATCH_FUNC,
0, 0, NULL, status_selection_changed, NULL);
g_signal_handlers_unblock_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC,
0, 0, NULL, status_text_changed, NULL);
}
static int
blist_node_compare_position(PurpleBlistNode *n1, PurpleBlistNode *n2)
{
while ((n1 = purple_blist_node_get_sibling_prev(n1)) != NULL)
if (n1 == n2)
return 1;
return -1;
}
static int
blist_node_compare_text(PurpleBlistNode *n1, PurpleBlistNode *n2)
{
const char *s1, *s2;
char *us1, *us2;
int ret;
if (G_OBJECT_TYPE(n1) != G_OBJECT_TYPE(n2))
return blist_node_compare_position(n1, n2);
if (PURPLE_IS_CHAT(n1)) {
s1 = purple_chat_get_name((PurpleChat*)n1);
s2 = purple_chat_get_name((PurpleChat*)n2);
} else if (PURPLE_IS_BUDDY(n1)) {
return purple_buddy_presence_compare(
PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n1))),
PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n2))));
} else if (PURPLE_IS_CONTACT(n1)) {
s1 = purple_contact_get_alias((PurpleContact*)n1);
s2 = purple_contact_get_alias((PurpleContact*)n2);
} else {
return blist_node_compare_position(n1, n2);
}
us1 = g_utf8_strup(s1, -1);
us2 = g_utf8_strup(s2, -1);
ret = g_utf8_collate(us1, us2);
g_free(us1);
g_free(us2);
return ret;
}
static int
blist_node_compare_status(PurpleBlistNode *n1, PurpleBlistNode *n2)
{
int ret;
if (G_OBJECT_TYPE(n1) != G_OBJECT_TYPE(n2))
return blist_node_compare_position(n1, n2);
if (PURPLE_IS_CONTACT(n1))
n1 = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n1)));
if (PURPLE_IS_CONTACT(n2))
n2 = PURPLE_BLIST_NODE(purple_contact_get_priority_buddy(PURPLE_CONTACT(n2)));
if (PURPLE_IS_BUDDY(n1) && PURPLE_IS_BUDDY(n2)) {
ret = purple_buddy_presence_compare(
PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n1))),
PURPLE_BUDDY_PRESENCE(purple_buddy_get_presence(PURPLE_BUDDY(n2))));
if (ret != 0)
return ret;
} else {
return blist_node_compare_position(n1, n2);
}
/* Sort alphabetically if presence is not comparable */
ret = blist_node_compare_text(n1, n2);
return ret;
}
static int
get_contact_log_size(PurpleBlistNode *c)
{
int log = 0;
PurpleBlistNode *node;
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, purple_buddy_get_name(b),
purple_buddy_get_account(b));
}
return log;
}
static int
blist_node_compare_log(PurpleBlistNode *n1, PurpleBlistNode *n2)
{
int ret;
PurpleBuddy *b1, *b2;
if (G_OBJECT_TYPE(n1) != G_OBJECT_TYPE(n2))
return blist_node_compare_position(n1, n2);
if (PURPLE_IS_BUDDY(n1)) {
b1 = (PurpleBuddy*)n1;
b2 = (PurpleBuddy*)n2;
ret = purple_log_get_total_size(PURPLE_LOG_IM, purple_buddy_get_name(b2), purple_buddy_get_account(b2)) -
purple_log_get_total_size(PURPLE_LOG_IM, purple_buddy_get_name(b1), purple_buddy_get_account(b1));
if (ret != 0)
return ret;
} else if (PURPLE_IS_CONTACT(n1)) {
ret = get_contact_log_size(n2) - get_contact_log_size(n1);
if (ret != 0)
return ret;
} else {
return blist_node_compare_position(n1, n2);
}
ret = blist_node_compare_text(n1, n2);
return ret;
}
static void
plugin_action(GntMenuItem *item, gpointer data)
{
PurplePluginAction *action = data;
if (action && action->callback)
action->callback(action);
}
static void
build_plugin_actions(GntMenuItem *item, PurplePlugin *plugin)
{
GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
PurplePluginActionsCb actions_cb;
GList *actions;
GntMenuItem *menuitem;
actions_cb =
purple_plugin_info_get_actions_cb(purple_plugin_get_info(plugin));
gnt_menuitem_set_submenu(item, GNT_MENU(sub));
for (actions = actions_cb(plugin); actions;
actions = g_list_delete_link(actions, actions)) {
if (actions->data) {
PurplePluginAction *action = actions->data;
action->plugin = plugin;
menuitem = gnt_menuitem_new(action->label);
gnt_menu_add_item(GNT_MENU(sub), menuitem);
gnt_menuitem_set_callback(menuitem, plugin_action, action);
g_object_set_data_full(G_OBJECT(menuitem), "plugin_action",
action, (GDestroyNotify)purple_plugin_action_free);
}
}
}
static void
protocol_action(GntMenuItem *item, gpointer data)
{
PurpleProtocolAction *action = data;
if (action && action->callback)
action->callback(action);
}
static void
build_protocol_actions(GntMenuItem *item, PurpleProtocol *protocol,
PurpleConnection *gc)
{
GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP);
GList *actions;
GntMenuItem *menuitem;
gnt_menuitem_set_submenu(item, GNT_MENU(sub));
for (actions = purple_protocol_client_get_actions(PURPLE_PROTOCOL_CLIENT(protocol), gc);
actions; actions = g_list_delete_link(actions, actions)) {
if (actions->data) {
PurpleProtocolAction *action = actions->data;
action->connection = gc;
menuitem = gnt_menuitem_new(action->label);
gnt_menu_add_item(GNT_MENU(sub), menuitem);
gnt_menuitem_set_callback(menuitem, protocol_action, action);
g_object_set_data_full(G_OBJECT(menuitem), "protocol_action",
action, (GDestroyNotify)purple_protocol_action_free);
}
}
}
static gboolean
buddy_recent_signed_on_off(gpointer data)
{
PurpleBlistNode *node = data;
FinchBlistNode *fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
g_source_remove(fnode->signed_timer);
fnode->signed_timer = 0;
if (!ggblist->manager->can_add_node(node)) {
node_remove(purple_blist_get_default(), node);
} else {
update_node_display(node, ggblist);
if (purple_blist_node_get_parent(node) && PURPLE_IS_CONTACT(purple_blist_node_get_parent(node)))
update_node_display(purple_blist_node_get_parent(node), ggblist);
}
g_object_unref(node);
return FALSE;
}
static gboolean
buddy_signed_on_off_cb(gpointer data)
{
PurpleBlistNode *node = data;
FinchBlistNode *fnode = g_object_get_data(G_OBJECT(node), UI_DATA);
if(!ggblist || !fnode) {
return FALSE;
}
if(fnode->signed_timer) {
g_source_remove(fnode->signed_timer);
}
g_object_ref(node);
fnode->signed_timer = g_timeout_add_seconds(6, (GSourceFunc)buddy_recent_signed_on_off, data);
update_node_display(node, ggblist);
if (purple_blist_node_get_parent(node) && PURPLE_IS_CONTACT(purple_blist_node_get_parent(node)))
update_node_display(purple_blist_node_get_parent(node), ggblist);
return FALSE;
}
static void
buddy_signed_on_off(PurpleBuddy* buddy, gpointer null)
{
g_idle_add(buddy_signed_on_off_cb, buddy);
}
static void
reconstruct_plugins_menu(void)
{
GntWidget *sub;
GntMenuItem *plg;
GList *iter;
if (!ggblist)
return;
if (ggblist->plugins == NULL)
ggblist->plugins = gnt_menuitem_new(_("Plugins"));
plg = ggblist->plugins;
sub = gnt_menu_new(GNT_MENU_POPUP);
gnt_menuitem_set_submenu(plg, GNT_MENU(sub));
for (iter = purple_plugins_get_loaded(); iter; iter = iter->next) {
PurplePlugin *plugin = iter->data;
PurplePluginInfo *info = purple_plugin_get_info(plugin);
GntMenuItem *item;
if (!purple_plugin_info_get_actions_cb(info))
continue;
item = gnt_menuitem_new(_(gplugin_plugin_info_get_name(
GPLUGIN_PLUGIN_INFO(info))));
gnt_menu_add_item(GNT_MENU(sub), item);
build_plugin_actions(item, plugin);
}
}
static void
reconstruct_plugins_menu_cb(GObject *plugin_manager, GPluginPlugin *plugin,
gpointer data)
{
reconstruct_plugins_menu();
}
static void
reconstruct_accounts_menu(void)
{
GntWidget *sub;
GntMenuItem *acc, *item;
GList *iter;
if (!ggblist)
return;
if (ggblist->accounts == NULL)
ggblist->accounts = gnt_menuitem_new(_("Accounts"));
acc = ggblist->accounts;
sub = gnt_menu_new(GNT_MENU_POPUP);
gnt_menuitem_set_submenu(acc, GNT_MENU(sub));
for (iter = purple_accounts_get_all_active(); iter;
iter = g_list_delete_link(iter, iter)) {
PurpleAccount *account = iter->data;
PurpleConnection *gc = purple_account_get_connection(account);
PurpleProtocol *protocol;
if (!gc || !PURPLE_CONNECTION_IS_CONNECTED(gc))
continue;
protocol = purple_connection_get_protocol(gc);
if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions)) {
item = gnt_menuitem_new(purple_account_get_username(account));
gnt_menu_add_item(GNT_MENU(sub), item);
build_protocol_actions(item, protocol, gc);
}
}
}
static void
reconstruct_grouping_menu(void)
{
GList *iter;
GntWidget *subsub;
if (!ggblist || !ggblist->grouping)
return;
subsub = gnt_menu_new(GNT_MENU_POPUP);
gnt_menuitem_set_submenu(ggblist->grouping, GNT_MENU(subsub));
for (iter = managers; iter; iter = iter->next) {
char menuid[128];
FinchBlistManager *manager = iter->data;
GntMenuItem *item = gnt_menuitem_new(_(manager->name));
g_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);
}
}
static gboolean
auto_join_chats(gpointer data)
{
PurpleBlistNode *node;
PurpleConnection *pc = data;
PurpleAccount *account = purple_connection_get_account(pc);
for (node = purple_blist_get_default_root(); node;
node = purple_blist_node_next(node, FALSE)) {
if (PURPLE_IS_CHAT(node)) {
PurpleChat *chat = (PurpleChat*)node;
if (purple_chat_get_account(chat) == account &&
purple_blist_node_get_bool(node, "gnt-autojoin"))
purple_serv_join_chat(purple_account_get_connection(account), purple_chat_get_components(chat));
}
}
return FALSE;
}
static gboolean
account_autojoin_cb(PurpleConnection *gc, gpointer null)
{
g_idle_add(auto_join_chats, gc);
return TRUE;
}
static void toggle_pref_cb(GntMenuItem *item, gpointer n)
{
purple_prefs_set_bool(n, !purple_prefs_get_bool(n));
}
static void sort_blist_change_cb(GntMenuItem *item, gpointer n)
{
purple_prefs_set_string(PREF_ROOT "/sort_type", n);
}
static void
block_select_cb(gpointer data, PurpleRequestFields *fields)
{
PurpleAccount *account = purple_request_fields_get_account(fields, "account");
const char *name = purple_request_fields_get_string(fields, "screenname");
if (account && name && *name != '\0') {
if (GPOINTER_TO_INT(purple_request_fields_get_choice(fields, "block")) == 1) {
purple_account_privacy_deny(account, name);
} else {
purple_account_privacy_allow(account, name);
}
}
}
static void
block_select(GntMenuItem *item, gpointer n)
{
PurpleRequestFields *fields;
PurpleRequestFieldGroup *group;
PurpleRequestField *field;
fields = purple_request_fields_new();
group = purple_request_field_group_new(NULL);
purple_request_fields_add_group(fields, group);
field = purple_request_field_string_new("screenname", _("Name"), NULL, FALSE);
purple_request_field_set_type_hint(field, "screenname");
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
field = purple_request_field_account_new("account", _("Account"), NULL);
purple_request_field_set_type_hint(field, "account");
purple_request_field_set_visible(field,
(purple_connections_get_all() != NULL &&
purple_connections_get_all()->next != NULL));
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
field = purple_request_field_choice_new("block", _("Block/Unblock"), GINT_TO_POINTER(1));
purple_request_field_choice_add(field, _("Block"), GINT_TO_POINTER(1));
purple_request_field_choice_add(field, _("Unblock"), GINT_TO_POINTER(2));
purple_request_field_group_add_field(group, field);
purple_request_fields(
purple_blist_get_default(), _("Block/Unblock"), NULL,
_("Please enter the username or alias of the person "
"you would like to Block/Unblock."),
fields, _("OK"), G_CALLBACK(block_select_cb), _("Cancel"), NULL,
NULL, NULL);
}
/* send_im_select* -- Xerox */
static void
send_im_select_cb(gpointer data, PurpleRequestFields *fields)
{
PurpleAccount *account;
const char *username;
PurpleConversation *im;
account = purple_request_fields_get_account(fields, "account");
username = purple_request_fields_get_string(fields, "screenname");
im = purple_im_conversation_new(account, username);
purple_conversation_present(im);
}
static void
send_im_select(GntMenuItem *item, gpointer n)
{
PurpleRequestFields *fields;
PurpleRequestFieldGroup *group;
PurpleRequestField *field;
fields = purple_request_fields_new();
group = purple_request_field_group_new(NULL);
purple_request_fields_add_group(fields, group);
field = purple_request_field_string_new("screenname", _("Name"), NULL, FALSE);
purple_request_field_set_type_hint(field, "screenname");
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
field = purple_request_field_account_new("account", _("Account"), NULL);
purple_request_field_set_type_hint(field, "account");
purple_request_field_set_visible(field,
(purple_connections_get_all() != NULL &&
purple_connections_get_all()->next != NULL));
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
purple_request_fields(
purple_blist_get_default(), _("New Instant Message"), NULL,
_("Please enter the username or alias of the person "
"you would like to IM."),
fields, _("OK"), G_CALLBACK(send_im_select_cb), _("Cancel"),
NULL, NULL, NULL);
}
static void
join_chat_select_cb(gpointer data, PurpleRequestFields *fields)
{
PurpleAccount *account;
const char *name;
PurpleConnection *gc;
PurpleConversationManager *manager;
PurpleChat *chat;
GHashTable *hash = NULL;
PurpleConversation *conv;
account = purple_request_fields_get_account(fields, "account");
name = purple_request_fields_get_string(fields, "chat");
if (!purple_account_is_connected(account))
return;
gc = purple_account_get_connection(account);
manager = purple_conversation_manager_get_default();
/* Create a new conversation now. This will give focus to the new window.
* But it's necessary to pretend that we left the chat, because otherwise
* a new conversation window will pop up when we finally join the chat. */
conv = purple_conversation_manager_find_chat(manager, account, name);
if(!PURPLE_IS_CHAT_CONVERSATION(conv)) {
conv = purple_chat_conversation_new(account, name);
purple_chat_conversation_leave(PURPLE_CHAT_CONVERSATION(conv));
} else {
purple_conversation_present(conv);
}
chat = purple_blist_find_chat(account, name);
if (chat == NULL) {
PurpleProtocol *protocol = purple_connection_get_protocol(gc);
hash = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol), gc, name);
} else {
hash = purple_chat_get_components(chat);
}
purple_serv_join_chat(gc, hash);
if (chat == NULL && hash != NULL)
g_hash_table_destroy(hash);
}
static void
join_chat_select(GntMenuItem *item, gpointer n)
{
PurpleRequestFields *fields;
PurpleRequestFieldGroup *group;
PurpleRequestField *field;
fields = purple_request_fields_new();
group = purple_request_field_group_new(NULL);
purple_request_fields_add_group(fields, group);
field = purple_request_field_string_new("chat", _("Channel"), NULL, FALSE);
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
field = purple_request_field_account_new("account", _("Account"), NULL);
purple_request_field_set_type_hint(field, "account");
purple_request_field_set_visible(field,
(purple_connections_get_all() != NULL &&
purple_connections_get_all()->next != NULL));
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
purple_request_fields(
purple_blist_get_default(), _("Join a Chat"), NULL,
_("Please enter the name of the chat you want to join."),
fields, _("Join"), G_CALLBACK(join_chat_select_cb), _("Cancel"),
NULL, NULL, NULL);
}
static void
view_log_select_cb(gpointer data, PurpleRequestFields *fields)
{
PurpleAccount *account;
const char *name;
PurpleBuddy *buddy;
PurpleContact *contact;
account = purple_request_fields_get_account(fields, "account");
name = purple_request_fields_get_string(fields, "screenname");
buddy = purple_blist_find_buddy(account, name);
if (buddy) {
contact = purple_buddy_get_contact(buddy);
} else {
contact = NULL;
}
if (contact) {
finch_log_show_contact(contact);
} else {
finch_log_show(PURPLE_LOG_IM, name, account);
}
}
static void
view_log_cb(GntMenuItem *item, gpointer n)
{
PurpleRequestFields *fields;
PurpleRequestFieldGroup *group;
PurpleRequestField *field;
fields = purple_request_fields_new();
group = purple_request_field_group_new(NULL);
purple_request_fields_add_group(fields, group);
field = purple_request_field_string_new("screenname", _("Name"), NULL, FALSE);
purple_request_field_set_type_hint(field, "screenname-all");
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
field = purple_request_field_account_new("account", _("Account"), NULL);
purple_request_field_set_type_hint(field, "account");
purple_request_field_set_visible(field,
(purple_accounts_get_all() != NULL &&
purple_accounts_get_all()->next != NULL));
purple_request_field_set_required(field, TRUE);
purple_request_field_group_add_field(group, field);
purple_request_field_account_set_show_all(field, TRUE);
purple_request_fields(
purple_blist_get_default(), _("View Log"), NULL,
_("Please enter the username or alias of the person "
"whose log you would like to view."),
fields, _("OK"), G_CALLBACK(view_log_select_cb), _("Cancel"),
NULL, NULL, NULL);
}
static void
view_all_logs_cb(GntMenuItem *item, gpointer n)
{
finch_log_show(PURPLE_LOG_IM, NULL, NULL);
}
static void
menu_add_buddy_cb(GntMenuItem *item, gpointer null)
{
purple_blist_request_add_buddy(NULL, NULL, NULL, NULL);
}
static void
menu_add_chat_cb(GntMenuItem *item, gpointer null)
{
purple_blist_request_add_chat(NULL, NULL, NULL, NULL);
}
static void
menu_add_group_cb(GntMenuItem *item, gpointer null)
{
purple_blist_request_add_group();
}
static void
menu_group_set_cb(GntMenuItem *item, gpointer null)
{
const char *id = g_object_get_data(G_OBJECT(item), "grouping-id");
purple_prefs_set_string(PREF_ROOT "/grouping", id);
}
static void
create_menu(void)
{
GntWidget *menu, *sub, *subsub;
GntMenuItem *item;
GntWindow *window;
if (!ggblist)
return;
window = GNT_WINDOW(ggblist->window);
ggblist->menu = menu = gnt_menu_new(GNT_MENU_TOPLEVEL);
gnt_window_set_menu(window, GNT_MENU(menu));
item = gnt_menuitem_new(_("Options"));
gnt_menu_add_item(GNT_MENU(menu), item);
sub = gnt_menu_new(GNT_MENU_POPUP);
gnt_menuitem_set_submenu(item, GNT_MENU(sub));
item = gnt_menuitem_new(_("Send IM..."));
gnt_menuitem_set_id(GNT_MENU_ITEM(item), "send-im");
gnt_menu_add_item(GNT_MENU(sub), item);
gnt_menuitem_set_callback(GNT_MENU_ITEM(item), send_im_select, NULL);
item = gnt_menuitem_new(_("Block/Unblock..."));
gnt_menuitem_set_id(GNT_MENU_ITEM(item), "block-unblock");
gnt_menu_add_item(GNT_MENU(sub), item);
gnt_menuitem_set_callback(GNT_MENU_ITEM(item), block_select, NULL);
item = gnt_menuitem_new(_("Join Chat..."));
gnt_menuitem_set_id(GNT_MENU_ITEM(item), "join-chat");
gnt_menu_add_item(GNT_MENU(sub), item);
gnt_menuitem_set_callback(GNT_MENU_ITEM(item), join_chat_select, NULL);
item = gnt_menuitem_new(_("View Log..."));
gnt_menuitem_set_id(GNT_MENU_ITEM(item), "view-log");
gnt_menu_add_item(GNT_MENU(sub), item);
gnt_menuitem_set_callback(GNT_MENU_ITEM(item), view_log_cb, NULL);
item = gnt_menuitem_new(_("View All Logs"));
gnt_menuitem_set_id(GNT_MENU_ITEM(item), "view-all-logs");
gnt_menu_add_item(GNT_MENU(sub), item);
gnt_menuitem_set_callback(GNT_MENU_ITEM(item), view_all_logs_cb, NULL);
item = gnt_menuitem_new(_("Show"));
gnt_menu_add_item(GNT_MENU(sub), item);
subsub = gnt_menu_new(GNT_MENU_POPUP);
gnt_menuitem_set_submenu(item, GNT_MENU(subsub));
item = gnt_menuitem_check_new(_("Empty groups"));
gnt_menuitem_set_id(GNT_MENU_ITEM(item), "show-empty-groups");
gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item),
purple_prefs_get_bool(PREF_ROOT "/emptygroups"));
gnt_menu_add_item(GNT_MENU(subsub), item);
gnt_menuitem_set_callback(GNT_MENU_ITEM(item), toggle_pref_cb, PREF_ROOT "/emptygroups");
item = gnt_menuitem_check_new(_("Offline buddies"));
gnt_menuitem_set_id(GNT_MENU_ITEM(item), "show-offline-buddies");
gnt_menuitem_check_set_checked(GNT_MENU_ITEM_CHECK(item),
purple_prefs_get_bool(PREF_ROOT "/showoffline"));
gnt_menu_add_item(GNT_MENU(subsub), item);
gnt_menuitem_set_callback(GNT_MENU_ITEM(item), toggle_pref_cb, PREF_ROOT "/showoffline");
item = gnt_menuitem_new(_("Sort"));
gnt_menu_add_item(GNT_MENU(sub), item);
subsub = gnt_menu_new(GNT_MENU_POPUP);
gnt_menuitem_set_submenu(item, GNT_MENU(subsub));
item = gnt_menuitem_new(_("By Status"));
gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-status");
gnt_menu_add_item(GNT_MENU(subsub), item);
gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "status");
item = gnt_menuitem_new(_("Alphabetically"));
gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-alpha");
gnt_menu_add_item(GNT_MENU(subsub), item);
gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "text");
item = gnt_menuitem_new(_("By Log Size"));
gnt_menuitem_set_id(GNT_MENU_ITEM(item), "sort-log");
gnt_menu_add_item(GNT_MENU(subsub), item);
gnt_menuitem_set_callback(GNT_MENU_ITEM(item), sort_blist_change_cb, "log");
item = gnt_menuitem_new(_("Add"));
gnt_menu_add_item(GNT_MENU(sub), item);
subsub = gnt_menu_new(GNT_MENU_POPUP);
gnt_menuitem_set_submenu(item, GNT_MENU(subsub));
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"));
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"));
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);
ggblist->grouping = item = gnt_menuitem_new(_("Grouping"));
gnt_menu_add_item(GNT_MENU(sub), item);
reconstruct_grouping_menu();
reconstruct_accounts_menu();
gnt_menu_add_item(GNT_MENU(menu), ggblist->accounts);
reconstruct_plugins_menu();
gnt_menu_add_item(GNT_MENU(menu), ggblist->plugins);
}
void finch_blist_show()
{
blist_show(purple_blist_get_default());
}
static void
group_collapsed(GntWidget *widget, PurpleBlistNode *node, gboolean collapsed, gpointer null)
{
if (PURPLE_IS_GROUP(node))
purple_blist_node_set_bool(node, "collapsed", collapsed);
}
static void
blist_show(PurpleBuddyList *list)
{
GPluginManager *plugin_manager = NULL;
if (ggblist->window) {
gnt_window_present(ggblist->window);
return;
}
ggblist->window = gnt_vwindow_new(FALSE);
gnt_widget_set_name(ggblist->window, "buddylist");
gnt_box_set_toplevel(GNT_BOX(ggblist->window), TRUE);
gnt_box_set_title(GNT_BOX(ggblist->window), _("Buddy List"));
gnt_box_set_pad(GNT_BOX(ggblist->window), 0);
ggblist->tree = gnt_tree_new();
gnt_widget_set_has_border(ggblist->tree, FALSE);
gnt_widget_set_size(ggblist->tree, purple_prefs_get_int(PREF_ROOT "/size/width"),
purple_prefs_get_int(PREF_ROOT "/size/height"));
gnt_widget_set_position(ggblist->window, purple_prefs_get_int(PREF_ROOT "/position/x"),
purple_prefs_get_int(PREF_ROOT "/position/y"));
gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->tree);
ggblist->status = gnt_combo_box_new();
gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->status);
ggblist->statustext = gnt_entry_new(NULL);
gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->statustext);
gnt_widget_show(ggblist->window);
purple_signal_connect(purple_connections_get_handle(), "signed-on", finch_blist_get_handle(),
PURPLE_CALLBACK(reconstruct_accounts_menu), NULL);
purple_signal_connect(purple_connections_get_handle(), "signed-off", finch_blist_get_handle(),
PURPLE_CALLBACK(reconstruct_accounts_menu), NULL);
purple_signal_connect(purple_accounts_get_handle(), "account-actions-changed", finch_blist_get_handle(),
PURPLE_CALLBACK(reconstruct_accounts_menu), NULL);
purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", finch_blist_get_handle(),
PURPLE_CALLBACK(buddy_status_changed), ggblist);
purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", finch_blist_get_handle(),
PURPLE_CALLBACK(buddy_idle_changed), ggblist);
plugin_manager = gplugin_manager_get_instance();
g_signal_connect_object(plugin_manager, "loaded-plugin",
G_CALLBACK(reconstruct_plugins_menu_cb), ggblist, 0);
g_signal_connect_object(plugin_manager, "unloaded-plugin",
G_CALLBACK(reconstruct_plugins_menu_cb), ggblist, 0);
purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", finch_blist_get_handle(),
PURPLE_CALLBACK(buddy_signed_on_off), ggblist);
purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", finch_blist_get_handle(),
PURPLE_CALLBACK(buddy_signed_on_off), ggblist);
g_signal_connect(G_OBJECT(ggblist->tree), "selection_changed", G_CALLBACK(selection_changed), ggblist);
g_signal_connect(G_OBJECT(ggblist->tree), "key_pressed", G_CALLBACK(key_pressed), ggblist);
g_signal_connect(G_OBJECT(ggblist->tree), "context-menu", G_CALLBACK(context_menu), ggblist);
g_signal_connect(G_OBJECT(ggblist->tree), "collapse-toggled", G_CALLBACK(group_collapsed), NULL);
g_signal_connect(G_OBJECT(ggblist->tree), "activate", G_CALLBACK(selection_activate), ggblist);
g_signal_connect_data(G_OBJECT(ggblist->tree), "gained-focus", G_CALLBACK(draw_tooltip),
ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_connect_data(G_OBJECT(ggblist->tree), "lost-focus", G_CALLBACK(remove_peripherals),
ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_connect_data(G_OBJECT(ggblist->window), "workspace-hidden", G_CALLBACK(remove_peripherals),
ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
g_signal_connect(G_OBJECT(ggblist->tree), "size_changed", G_CALLBACK(size_changed_cb), NULL);
g_signal_connect(G_OBJECT(ggblist->window), "position_set", G_CALLBACK(save_position_cb), NULL);
g_signal_connect(G_OBJECT(ggblist->window), "destroy", G_CALLBACK(reset_blist_window), NULL);
/* Status signals */
purple_signal_connect(purple_savedstatuses_get_handle(), "savedstatus-changed", finch_blist_get_handle(),
PURPLE_CALLBACK(savedstatus_changed), NULL);
g_signal_connect(G_OBJECT(ggblist->status), "selection_changed",
G_CALLBACK(status_selection_changed), NULL);
g_signal_connect(G_OBJECT(ggblist->statustext), "key_pressed",
G_CALLBACK(status_text_changed), NULL);
create_menu();
populate_buddylist();
savedstatus_changed(purple_savedstatus_get_current(), NULL);
}
void finch_blist_uninit()
{
}
gboolean finch_blist_get_position(int *x, int *y)
{
if (!ggblist || !ggblist->window)
return FALSE;
gnt_widget_get_position(ggblist->window, x, y);
return TRUE;
}
void finch_blist_set_position(int x, int y)
{
gnt_widget_set_position(ggblist->window, x, y);
}
gboolean finch_blist_get_size(int *width, int *height)
{
if (!ggblist || !ggblist->window)
return FALSE;
gnt_widget_get_size(ggblist->window, width, height);
return TRUE;
}
void finch_blist_set_size(int width, int height)
{
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);
reconstruct_grouping_menu();
if (purple_strequal(manager->id, purple_prefs_get_string(PREF_ROOT "/grouping")))
purple_prefs_trigger_callback(PREF_ROOT "/grouping");
}
}
void finch_blist_uninstall_manager(const FinchBlistManager *manager)
{
if (g_list_find(managers, manager)) {
managers = g_list_remove(managers, manager);
reconstruct_grouping_menu();
if (purple_strequal(manager->id, purple_prefs_get_string(PREF_ROOT "/grouping")))
purple_prefs_trigger_callback(PREF_ROOT "/grouping");
}
}
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 (purple_strequal(id, m->id))
return m;
}
return NULL;
}
GntTree * finch_blist_get_tree(void)
{
return ggblist ? GNT_TREE(ggblist->tree) : NULL;
}
/**************************************************************************
* GObject code
**************************************************************************/
G_DEFINE_TYPE(FinchBuddyList, finch_buddy_list, PURPLE_TYPE_BUDDY_LIST)
static void
finch_buddy_list_init(FinchBuddyList *self)
{
if (!ggblist) {
/* The first buddy list object becomes the default. */
ggblist = self;
}
self->manager = finch_blist_manager_find(
purple_prefs_get_string(PREF_ROOT "/grouping"));
if (!self->manager) {
self->manager = &default_manager;
}
}
static void
finch_buddy_list_finalize(GObject *obj)
{
FinchBuddyList *ggblist = FINCH_BUDDY_LIST(obj);
gnt_widget_destroy(ggblist->window);
G_OBJECT_CLASS(finch_buddy_list_parent_class)->finalize(obj);
}
static void
finch_buddy_list_class_init(FinchBuddyListClass *klass)
{
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
PurpleBuddyListClass *purple_blist_class;
obj_class->finalize = finch_buddy_list_finalize;
purple_blist_class = PURPLE_BUDDY_LIST_CLASS(klass);
purple_blist_class->new_node = new_node;
purple_blist_class->show = blist_show;
purple_blist_class->update = node_update;
purple_blist_class->remove = node_remove;
purple_blist_class->request_add_buddy = finch_request_add_buddy;
purple_blist_class->request_add_chat = finch_request_add_chat;
purple_blist_class->request_add_group = finch_request_add_group;
}
/**************************************************************************
* GBoxed code
**************************************************************************/
static FinchBlistManager *
finch_blist_manager_copy(FinchBlistManager *manager)
{
FinchBlistManager *manager_new;
g_return_val_if_fail(manager != NULL, NULL);
manager_new = g_new(FinchBlistManager, 1);
*manager_new = *manager;
return manager_new;
}
static void
finch_blist_manager_free(FinchBlistManager *manager)
{
g_return_if_fail(manager != NULL);
g_free(manager);
}
GType
finch_blist_manager_get_type(void)
{
static GType type = 0;
if (type == 0) {
type = g_boxed_type_register_static("FinchBlistManager",
(GBoxedCopyFunc)finch_blist_manager_copy,
(GBoxedFreeFunc)finch_blist_manager_free);
}
return type;
}