# HG changeset patch # User Christian Hammond # Date 1051379783 0 # Node ID 1a53330dfd3452369dd682353ffd6f7af8dd0d31 # Parent 6d1707dc8c3da7076fb3f02f00cc29553e9f99ea [gaim-migrate @ 5598] People have been talking about doing it, but nobody has done it. Since I want to clean up some header files today: list.[ch] -> blist.[ch] gtklist.h -> gtkblist.h buddy.c -> gtkblist.c committer: Tailor Script diff -r 6d1707dc8c3d -r 1a53330dfd34 src/Makefile.am --- a/src/Makefile.am Sat Apr 26 17:36:52 2003 +0000 +++ b/src/Makefile.am Sat Apr 26 17:56:23 2003 +0000 @@ -6,6 +6,8 @@ SUBDIRS = protocols CORESOURCES = \ + blist.c \ + blist.h \ conversation.c \ conversation.h \ core.c \ @@ -36,7 +38,6 @@ about.c \ away.c \ browser.c \ - buddy.c \ buddy_chat.c \ dialogs.c \ dnd-hints.c \ @@ -48,6 +49,8 @@ gaimrc.c \ gtkcellrendererprogress.c \ gtkcellrendererprogress.h \ + gtkblist.c \ + gtkblist.h \ gtkconv.c \ gtkconv.h \ gtkdebug.c \ @@ -56,7 +59,6 @@ gtkft.h \ gtkimhtml.c \ gtkimhtml.h \ - gtklist.h \ gtkplugin.c \ gtkplugin.h \ gtkpounce.c \ @@ -65,8 +67,6 @@ gtkutils.h \ html.c \ idle.c \ - list.c \ - list.h \ log.c \ main.c \ md5.c \ diff -r 6d1707dc8c3d -r 1a53330dfd34 src/away.c --- a/src/away.c Sat Apr 26 17:36:52 2003 +0000 +++ b/src/away.c Sat Apr 26 17:56:23 2003 +0000 @@ -31,7 +31,7 @@ #include "gaim.h" #include "prpl.h" #include "gtkimhtml.h" -#include "gtklist.h" +#include "gtkblist.h" #include "plugin.h" GtkWidget *imaway = NULL; diff -r 6d1707dc8c3d -r 1a53330dfd34 src/blist.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/blist.c Sat Apr 26 17:56:23 2003 +0000 @@ -0,0 +1,1546 @@ +/* + * gaim + * + * Copyright (C) 2003, Sean Egan + * Copyright (C) 1998-1999, Mark Spencer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#ifndef _WIN32 +#include +#else +#include +#endif +#include +#include "gaim.h" +#include "prpl.h" +#include "blist.h" + +#ifdef _WIN32 +#include "win32dep.h" +#endif + +#define PATHSIZE 1024 + +struct gaim_buddy_list *gaimbuddylist = NULL; +static struct gaim_blist_ui_ops *blist_ui_ops = NULL; + +/***************************************************************************** + * Private Utility functions * + *****************************************************************************/ +static GaimBlistNode *gaim_blist_get_last_sibling(GaimBlistNode *node) +{ + GaimBlistNode *n = node; + if (!n) + return NULL; + while (n->next) + n = n->next; + return n; +} +static GaimBlistNode *gaim_blist_get_last_child(GaimBlistNode *node) +{ + if (!node) + return NULL; + return gaim_blist_get_last_sibling(node->child); +} + +/***************************************************************************** + * Public API functions * + *****************************************************************************/ + +struct gaim_buddy_list *gaim_blist_new() +{ + struct gaim_buddy_list *gbl = g_new0(struct gaim_buddy_list, 1); + + gbl->ui_ops = gaim_get_blist_ui_ops(); + + if (gbl->ui_ops != NULL && gbl->ui_ops->new_list != NULL) + gbl->ui_ops->new_list(gbl); + + return gbl; +} + +void +gaim_set_blist(struct gaim_buddy_list *list) +{ + gaimbuddylist = list; +} + +struct gaim_buddy_list * +gaim_get_blist(void) +{ + return gaimbuddylist; +} + +void gaim_blist_show () +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + if (ops) + ops->show(gaimbuddylist); +} + +void gaim_blist_destroy() +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + if (ops) + ops->destroy(gaimbuddylist); +} + +void gaim_blist_set_visible (gboolean show) +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + if (ops) + ops->set_visible(gaimbuddylist, show); +} + +void gaim_blist_update_buddy_status (struct buddy *buddy, int status) +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + buddy->uc = status; + + if(!(status & UC_UNAVAILABLE)) + gaim_event_broadcast(event_buddy_back, buddy->account->gc, buddy->name); + else + gaim_event_broadcast(event_buddy_away, buddy->account->gc, buddy->name); + + if (ops) + ops->update(gaimbuddylist, (GaimBlistNode*)buddy); +} + +static gboolean presence_update_timeout_cb(struct buddy *buddy) { + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + + if(buddy->present == GAIM_BUDDY_SIGNING_ON) { + buddy->present = GAIM_BUDDY_ONLINE; + gaim_event_broadcast(event_buddy_signon, buddy->account->gc, buddy->name); + } else if(buddy->present == GAIM_BUDDY_SIGNING_OFF) { + buddy->present = GAIM_BUDDY_OFFLINE; + } + + buddy->timer = 0; + + if (ops) + ops->update(gaimbuddylist, (GaimBlistNode*)buddy); + + return FALSE; +} + +void gaim_blist_update_buddy_presence(struct buddy *buddy, int presence) { + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + gboolean do_timer = FALSE; + + if (!GAIM_BUDDY_IS_ONLINE(buddy) && presence) { + buddy->present = GAIM_BUDDY_SIGNING_ON; + gaim_event_broadcast(event_buddy_signon, buddy->account->gc, buddy->name); + do_timer = TRUE; + } else if(GAIM_BUDDY_IS_ONLINE(buddy) && !presence) { + buddy->present = GAIM_BUDDY_SIGNING_OFF; + gaim_event_broadcast(event_buddy_signoff, buddy->account->gc, buddy->name); + do_timer = TRUE; + } + + if(do_timer) { + if(buddy->timer > 0) + g_source_remove(buddy->timer); + buddy->timer = g_timeout_add(10000, (GSourceFunc)presence_update_timeout_cb, buddy); + } + + if (ops) + ops->update(gaimbuddylist, (GaimBlistNode*)buddy); +} + + +void gaim_blist_update_buddy_idle (struct buddy *buddy, int idle) +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + buddy->idle = idle; + if (ops) + ops->update(gaimbuddylist, (GaimBlistNode*)buddy); +} +void gaim_blist_update_buddy_evil (struct buddy *buddy, int warning) +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + buddy->evil = warning; + if (ops) + ops->update(gaimbuddylist,(GaimBlistNode*)buddy); +} +void gaim_blist_update_buddy_icon(struct buddy *buddy) { + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + if(ops) + ops->update(gaimbuddylist, (GaimBlistNode*)buddy); +} +void gaim_blist_rename_buddy (struct buddy *buddy, const char *name) +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + g_free(buddy->name); + buddy->name = g_strdup(name); + if (ops) + ops->update(gaimbuddylist, (GaimBlistNode*)buddy); +} +void gaim_blist_alias_buddy (struct buddy *buddy, const char *alias) +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + struct gaim_conversation *conv; + + g_free(buddy->alias); + + if(alias && strlen(alias)) + buddy->alias = g_strdup(alias); + else + buddy->alias = NULL; + + if (ops) + ops->update(gaimbuddylist, (GaimBlistNode*)buddy); + + conv = gaim_find_conversation_with_account(buddy->name, buddy->account); + + if (conv) + gaim_conversation_autoset_title(conv); +} + +void gaim_blist_rename_group(struct group *group, const char *name) +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + g_free(group->name); + group->name = g_strdup(name); + if (ops) + ops->update(gaimbuddylist, (GaimBlistNode*)group); +} +struct buddy *gaim_buddy_new(struct gaim_account *account, const char *screenname, const char *alias) +{ + struct buddy *b; + struct gaim_blist_ui_ops *ops; + + b = g_new0(struct buddy, 1); + b->account = account; + b->name = g_strdup(screenname); + b->alias = g_strdup(alias); + b->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + ((GaimBlistNode*)b)->type = GAIM_BLIST_BUDDY_NODE; + + ops = gaim_get_blist_ui_ops(); + + if (ops != NULL && ops->new_node != NULL) + ops->new_node((GaimBlistNode *)b); + + return b; +} +void gaim_blist_add_buddy (struct buddy *buddy, struct group *group, GaimBlistNode *node) +{ + GaimBlistNode *n = node, *bnode = (GaimBlistNode*)buddy; + struct group *g = group; + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + gboolean save = FALSE; + + if (!n) { + if (!g) { + g = gaim_group_new(_("Buddies")); + gaim_blist_add_group(g, NULL); + } + n = gaim_blist_get_last_child((GaimBlistNode*)g); + } else { + g = (struct group*)n->parent; + } + + /* if we're moving to overtop of ourselves, do nothing */ + if(bnode == n) + return; + + if (bnode->parent) { + /* This buddy was already in the list and is + * being moved. + */ + if(bnode->next) + bnode->next->prev = bnode->prev; + if(bnode->prev) + bnode->prev->next = bnode->next; + if(bnode->parent->child == bnode) + bnode->parent->child = bnode->next; + + ops->remove(gaimbuddylist, bnode); + + if (bnode->parent != ((GaimBlistNode*)g)) + serv_move_buddy(buddy, (struct group*)bnode->parent, g); + save = TRUE; + } + + if (n) { + if(n->next) + n->next->prev = (GaimBlistNode*)buddy; + ((GaimBlistNode*)buddy)->next = n->next; + ((GaimBlistNode*)buddy)->prev = n; + ((GaimBlistNode*)buddy)->parent = n->parent; + n->next = (GaimBlistNode*)buddy; + } else { + ((GaimBlistNode*)g)->child = (GaimBlistNode*)buddy; + ((GaimBlistNode*)buddy)->next = NULL; + ((GaimBlistNode*)buddy)->prev = NULL; + ((GaimBlistNode*)buddy)->parent = (GaimBlistNode*)g; + } + + if (ops) + ops->update(gaimbuddylist, (GaimBlistNode*)buddy); + if (save) + gaim_blist_save(); +} + +struct group *gaim_group_new(const char *name) +{ + struct group *g = gaim_find_group(name); + + if (!g) { + struct gaim_blist_ui_ops *ops; + g= g_new0(struct group, 1); + g->name = g_strdup(name); + g->settings = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + ((GaimBlistNode*)g)->type = GAIM_BLIST_GROUP_NODE; + + ops = gaim_get_blist_ui_ops(); + + if (ops != NULL && ops->new_node != NULL) + ops->new_node((GaimBlistNode *)g); + + } + return g; +} + +void gaim_blist_add_group (struct group *group, GaimBlistNode *node) +{ + struct gaim_blist_ui_ops *ops; + GaimBlistNode *gnode = (GaimBlistNode*)group; + gboolean save = FALSE; + + if (!gaimbuddylist) + gaimbuddylist = gaim_blist_new(); + ops = gaimbuddylist->ui_ops; + + if (!gaimbuddylist->root) { + gaimbuddylist->root = gnode; + return; + } + + if (!node) + node = gaim_blist_get_last_sibling(gaimbuddylist->root); + + /* if we're moving to overtop of ourselves, do nothing */ + if(gnode == node) + return; + + if (gaim_find_group(group->name)) { + /* This is just being moved */ + + ops->remove(gaimbuddylist, (GaimBlistNode*)group); + + if(gnode == gaimbuddylist->root) + gaimbuddylist->root = gnode->next; + if(gnode->prev) + gnode->prev->next = gnode->next; + if(gnode->next) + gnode->next->prev = gnode->prev; + + save = TRUE; + } + + gnode->next = node->next; + gnode->prev = node; + if(node->next) + node->next->prev = gnode; + node->next = gnode; + + if (ops) { + ops->update(gaimbuddylist, gnode); + for(node = gnode->child; node; node = node->next) + ops->update(gaimbuddylist, node); + } + if (save) + gaim_blist_save(); +} + +void gaim_blist_remove_buddy (struct buddy *buddy) +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + + GaimBlistNode *gnode, *node = (GaimBlistNode*)buddy; + struct group *group; + + gnode = node->parent; + group = (struct group *)gnode; + + if(gnode->child == node) + gnode->child = node->next; + if (node->prev) + node->prev->next = node->next; + if (node->next) + node->next->prev = node->prev; + + ops->remove(gaimbuddylist, node); + g_hash_table_destroy(buddy->settings); + g_free(buddy->name); + g_free(buddy->alias); + g_free(buddy); +} + +void gaim_blist_remove_group (struct group *group) +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + GaimBlistNode *node = (GaimBlistNode*)group; + + if(node->child) { + char *buf; + int count = 0; + GaimBlistNode *child = node->child; + + while(child) { + count++; + child = child->next; + } + + buf = g_strdup_printf(_("%d buddies from group %s were not " + "removed because their accounts were not logged in. These " + "buddies, and the group were not removed.\n"), + count, group->name); + do_error_dialog(_("Group Not Removed"), buf, GAIM_ERROR); + g_free(buf); + return; + } + + if(gaimbuddylist->root == node) + gaimbuddylist->root = node->next; + if (node->prev) + node->prev->next = node->next; + if (node->next) + node->next->prev = node->prev; + + ops->remove(gaimbuddylist, node); + g_free(group->name); + g_free(group); +} + +char *gaim_get_buddy_alias_only(struct buddy *b) { + if(!b) + return NULL; + if(b->alias && b->alias[0]) + return b->alias; + else if((misc_options & OPT_MISC_USE_SERVER_ALIAS) && b->server_alias) + return b->server_alias; + return NULL; +} + +char * gaim_get_buddy_alias (struct buddy *buddy) +{ + char *ret = gaim_get_buddy_alias_only(buddy); + if(!ret) + return buddy ? buddy->name : _("Unknown"); + return ret; + +} + +struct buddy *gaim_find_buddy(struct gaim_account *account, const char *name) +{ + GaimBlistNode *group; + GaimBlistNode *buddy; + char *norm_name = g_strdup(normalize(name)); + + if (!gaimbuddylist) + return NULL; + + group = gaimbuddylist->root; + while (group) { + buddy = group->child; + while (buddy) { + if (!gaim_utf8_strcasecmp(normalize(((struct buddy*)buddy)->name), norm_name) && account == ((struct buddy*)buddy)->account) { + g_free(norm_name); + return (struct buddy*)buddy; + } + buddy = buddy->next; + } + group = group->next; + } + g_free(norm_name); + return NULL; +} + +struct group *gaim_find_group(const char *name) +{ + GaimBlistNode *node; + if (!gaimbuddylist) + return NULL; + node = gaimbuddylist->root; + while(node) { + if (!strcmp(((struct group*)node)->name, name)) + return (struct group*)node; + node = node->next; + } + return NULL; +} +struct group *gaim_find_buddys_group(struct buddy *buddy) +{ + if (!buddy) + return NULL; + return (struct group*)(((GaimBlistNode*)buddy)->parent); +} + +GSList *gaim_group_get_accounts(struct group *g) +{ + GSList *l = NULL; + GaimBlistNode *child = ((GaimBlistNode *)g)->child; + + while (child) { + if (!g_slist_find(l, ((struct buddy*)child)->account)) + l = g_slist_append(l, ((struct buddy*)child)->account); + child = child->next; + } + return l; +} + +void gaim_blist_remove_account(struct gaim_account *account) +{ + struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; + GaimBlistNode *group = gaimbuddylist->root; + GaimBlistNode *buddy; + if (!gaimbuddylist) + return; + while (group) { + buddy = group->child; + while (buddy) { + if (account == ((struct buddy*)buddy)->account) { + ((struct buddy*)buddy)->present = GAIM_BUDDY_OFFLINE; + if(ops) + ops->remove(gaimbuddylist, buddy); + } + buddy = buddy->next; + } + group = group->next; + } +} + +void parse_toc_buddy_list(struct gaim_account *account, char *config) +{ + char *c; + char current[256]; + GList *bud = NULL; + + + if (config != NULL) { + + /* skip "CONFIG:" (if it exists) */ + c = strncmp(config + 6 /* sizeof(struct sflap_hdr) */ , "CONFIG:", strlen("CONFIG:")) ? + strtok(config, "\n") : + strtok(config + 6 /* sizeof(struct sflap_hdr) */ + strlen("CONFIG:"), "\n"); + do { + if (c == NULL) + break; + if (*c == 'g') { + char *utf8 = NULL; + utf8 = gaim_try_conv_to_utf8(c + 2); + if (utf8 == NULL) { + g_strlcpy(current, _("Invalid Groupname"), sizeof(current)); + } else { + g_strlcpy(current, utf8, sizeof(current)); + g_free(utf8); + } + if (!gaim_find_group(current)) { + struct group *g = gaim_group_new(current); + gaim_blist_add_group(g, NULL); + } + } else if (*c == 'b') { /*&& !gaim_find_buddy(user, c + 2)) {*/ + char nm[80], sw[388], *a, *utf8 = NULL; + + if ((a = strchr(c + 2, ':')) != NULL) { + *a++ = '\0'; /* nul the : */ + } + + g_strlcpy(nm, c + 2, sizeof(nm)); + if (a) { + utf8 = gaim_try_conv_to_utf8(a); + if (utf8 == NULL) { + gaim_debug(GAIM_DEBUG_ERROR, "toc blist", + "Failed to convert alias for " + "'%s' to UTF-8\n", nm); + } + } + if (utf8 == NULL) { + sw[0] = '\0'; + } else { + /* This can leave a partial sequence at the end, + * but who cares? */ + g_strlcpy(sw, utf8, sizeof(sw)); + g_free(utf8); + } + + if (!gaim_find_buddy(account, nm)) { + struct buddy *b = gaim_buddy_new(account, nm, sw); + struct group *g = gaim_find_group(current); + gaim_blist_add_buddy(b, g, NULL); + bud = g_list_append(bud, g_strdup(nm)); + } + } else if (*c == 'p') { + gaim_privacy_permit_add(account, c + 2); + } else if (*c == 'd') { + gaim_privacy_deny_add(account, c + 2); + } else if (!strncmp("toc", c, 3)) { + sscanf(c + strlen(c) - 1, "%d", &account->permdeny); + gaim_debug(GAIM_DEBUG_MISC, "toc blist", + "permdeny: %d\n", account->permdeny); + if (account->permdeny == 0) + account->permdeny = 1; + } else if (*c == 'm') { + sscanf(c + 2, "%d", &account->permdeny); + gaim_debug(GAIM_DEBUG_MISC, "toc blist", + "permdeny: %d\n", account->permdeny); + if (account->permdeny == 0) + account->permdeny = 1; + } + } while ((c = strtok(NULL, "\n"))); + + if(account->gc) { + if(bud) { + GList *node = bud; + serv_add_buddies(account->gc, bud); + while(node) { + g_free(node->data); + node = node->next; + } + } + serv_set_permit_deny(account->gc); + } + g_list_free(bud); + } +} + +#if 0 +/* translate an AIM 3 buddylist (*.lst) to a Gaim buddylist */ +static GString *translate_lst(FILE *src_fp) +{ + char line[BUF_LEN], *line2; + char *name; + int i; + + GString *dest = g_string_new("m 1\n"); + + while (fgets(line, BUF_LEN, src_fp)) { + line2 = g_strchug(line); + if (strstr(line2, "group") == line2) { + name = strpbrk(line2, " \t\n\r\f") + 1; + dest = g_string_append(dest, "g "); + for (i = 0; i < strcspn(name, "\n\r"); i++) + if (name[i] != '\"') + dest = g_string_append_c(dest, name[i]); + dest = g_string_append_c(dest, '\n'); + } + if (strstr(line2, "buddy") == line2) { + name = strpbrk(line2, " \t\n\r\f") + 1; + dest = g_string_append(dest, "b "); + for (i = 0; i < strcspn(name, "\n\r"); i++) + if (name[i] != '\"') + dest = g_string_append_c(dest, name[i]); + dest = g_string_append_c(dest, '\n'); + } + } + + return dest; +} + + +/* translate an AIM 4 buddylist (*.blt) to Gaim format */ +static GString *translate_blt(FILE *src_fp) +{ + int i; + char line[BUF_LEN]; + char *buddy; + + GString *dest = g_string_new("m 1\n"); + + while (strstr(fgets(line, BUF_LEN, src_fp), "Buddy") == NULL); + while (strstr(fgets(line, BUF_LEN, src_fp), "list") == NULL); + + while (1) { + fgets(line, BUF_LEN, src_fp); g_strchomp(line); + if (strchr(line, '}') != NULL) + break; + + if (strchr(line, '{') != NULL) { + /* Syntax starting with " {" */ + + dest = g_string_append(dest, "g "); + buddy = g_strchug(strtok(line, "{")); + for (i = 0; i < strlen(buddy); i++) + if (buddy[i] != '\"') + dest = g_string_append_c(dest, buddy[i]); + dest = g_string_append_c(dest, '\n'); + while (strchr(fgets(line, BUF_LEN, src_fp), '}') == NULL) { + gboolean pounce = FALSE; + char *e; + g_strchomp(line); + buddy = g_strchug(line); + gaim_debug(GAIM_DEBUG_MISC, "AIM 4 blt import", + "buddy: \"%s\"\n", buddy); + dest = g_string_append(dest, "b "); + if (strchr(buddy, '{') != NULL) { + /* buddy pounce, etc */ + char *pos = strchr(buddy, '{') - 1; + *pos = 0; + pounce = TRUE; + } + if ((e = strchr(buddy, '\"')) != NULL) { + *e = '\0'; + buddy++; + } + dest = g_string_append(dest, buddy); + dest = g_string_append_c(dest, '\n'); + if (pounce) + do + fgets(line, BUF_LEN, src_fp); + while (!strchr(line, '}')); + } + } else { + + /* Syntax "group buddy buddy ..." */ + buddy = g_strchug(strtok(line, " \n")); + dest = g_string_append(dest, "g "); + if (strchr(buddy, '\"') != NULL) { + dest = g_string_append(dest, &buddy[1]); + dest = g_string_append_c(dest, ' '); + buddy = g_strchug(strtok(NULL, " \n")); + while (strchr(buddy, '\"') == NULL) { + dest = g_string_append(dest, buddy); + dest = g_string_append_c(dest, ' '); + buddy = g_strchug(strtok(NULL, " \n")); + } + buddy[strlen(buddy) - 1] = '\0'; + dest = g_string_append(dest, buddy); + } else { + dest = g_string_append(dest, buddy); + } + dest = g_string_append_c(dest, '\n'); + while ((buddy = g_strchug(strtok(NULL, " \n"))) != NULL) { + dest = g_string_append(dest, "b "); + if (strchr(buddy, '\"') != NULL) { + dest = g_string_append(dest, &buddy[1]); + dest = g_string_append_c(dest, ' '); + buddy = g_strchug(strtok(NULL, " \n")); + while (strchr(buddy, '\"') == NULL) { + dest = g_string_append(dest, buddy); + dest = g_string_append_c(dest, ' '); + buddy = g_strchug(strtok(NULL, " \n")); + } + buddy[strlen(buddy) - 1] = '\0'; + dest = g_string_append(dest, buddy); + } else { + dest = g_string_append(dest, buddy); + } + dest = g_string_append_c(dest, '\n'); + } + } + } + + return dest; +} + +static GString *translate_gnomeicu(FILE *src_fp) +{ + char line[BUF_LEN]; + GString *dest = g_string_new("m 1\ng Buddies\n"); + + while (strstr(fgets(line, BUF_LEN, src_fp), "NewContacts") == NULL); + + while (fgets(line, BUF_LEN, src_fp)) { + char *eq; + g_strchomp(line); + if (line[0] == '\n' || line[0] == '[') + break; + eq = strchr(line, '='); + if (!eq) + break; + *eq = ':'; + eq = strchr(eq, ','); + if (eq) + *eq = '\0'; + dest = g_string_append(dest, "b "); + dest = g_string_append(dest, line); + dest = g_string_append_c(dest, '\n'); + } + + return dest; +} +#endif + +static gchar *get_screenname_filename(const char *name) +{ + gchar **split; + gchar *good; + gchar *ret; + + split = g_strsplit(name, G_DIR_SEPARATOR_S, -1); + good = g_strjoinv(NULL, split); + g_strfreev(split); + + ret = g_utf8_strup(good, -1); + + g_free(good); + + return ret; +} + +static gboolean gaim_blist_read(const char *filename); + + +static void do_import(struct gaim_account *account, const char *filename) +{ + GString *buf = NULL; + char first[64]; + char path[PATHSIZE]; + int len; + FILE *f; + struct stat st; + + if (filename) { + g_snprintf(path, sizeof(path), "%s", filename); + } else { + char *g_screenname = get_screenname_filename(account->username); + char *file = gaim_user_dir(); + int protocol = (account->protocol == GAIM_PROTO_OSCAR) ? (isalpha(account->username[0]) ? GAIM_PROTO_TOC : GAIM_PROTO_ICQ): account->protocol; + + if (file != (char *)NULL) { + sprintf(path, "%s" G_DIR_SEPARATOR_S "%s.%d.blist", file, g_screenname, protocol); + g_free(g_screenname); + } else { + g_free(g_screenname); + return; + } + } + + if (stat(path, &st)) { + gaim_debug(GAIM_DEBUG_ERROR, "blist import", "Unable to stat %s.\n", + path); + return; + } + + if (!(f = fopen(path, "r"))) { + gaim_debug(GAIM_DEBUG_ERROR, "blist import", "Unable to open %s.\n", + path); + return; + } + + fgets(first, 64, f); + + if ((first[0] == '\n') || (first[0] == '\r' && first[1] == '\n')) + fgets(first, 64, f); + +#if 0 + if (!g_strncasecmp(first, "str); + g_string_free(buf, TRUE); + } +} + +gboolean gaim_group_on_account(struct group *g, struct gaim_account *account) { + GaimBlistNode *bnode; + for(bnode = g->node.child; bnode; bnode = bnode->next) { + struct buddy *b = (struct buddy *)bnode; + if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) + continue; + if((!account && b->account->gc) || b->account == account) + return TRUE; + } + return FALSE; +} + +static gboolean blist_safe_to_write = FALSE; + +static char *blist_parser_group_name = NULL; +static char *blist_parser_person_name = NULL; +static char *blist_parser_account_name = NULL; +static int blist_parser_account_protocol = 0; +static char *blist_parser_buddy_name = NULL; +static char *blist_parser_buddy_alias = NULL; +static char *blist_parser_setting_name = NULL; +static char *blist_parser_setting_value = NULL; +static GHashTable *blist_parser_buddy_settings = NULL; +static GHashTable *blist_parser_group_settings = NULL; +static int blist_parser_privacy_mode = 0; +static GList *tag_stack = NULL; +enum { + BLIST_TAG_GAIM, + BLIST_TAG_BLIST, + BLIST_TAG_GROUP, + BLIST_TAG_PERSON, + BLIST_TAG_BUDDY, + BLIST_TAG_NAME, + BLIST_TAG_ALIAS, + BLIST_TAG_SETTING, + BLIST_TAG_PRIVACY, + BLIST_TAG_ACCOUNT, + BLIST_TAG_PERMIT, + BLIST_TAG_BLOCK, + BLIST_TAG_IGNORE +}; +static gboolean blist_parser_error_occurred = FALSE; + +static void blist_start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) { + int i; + + if(!strcmp(element_name, "gaim")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_GAIM)); + } else if(!strcmp(element_name, "blist")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BLIST)); + } else if(!strcmp(element_name, "group")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_GROUP)); + for(i=0; attribute_names[i]; i++) { + if(!strcmp(attribute_names[i], "name")) { + g_free(blist_parser_group_name); + blist_parser_group_name = g_strdup(attribute_values[i]); + } + } + if(blist_parser_group_name) { + struct group *g = gaim_group_new(blist_parser_group_name); + gaim_blist_add_group(g,NULL); + } + } else if(!strcmp(element_name, "person")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PERSON)); + for(i=0; attribute_names[i]; i++) { + if(!strcmp(attribute_names[i], "name")) { + g_free(blist_parser_person_name); + blist_parser_person_name = g_strdup(attribute_values[i]); + } + } + } else if(!strcmp(element_name, "buddy")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BUDDY)); + for(i=0; attribute_names[i]; i++) { + if(!strcmp(attribute_names[i], "account")) { + g_free(blist_parser_account_name); + blist_parser_account_name = g_strdup(attribute_values[i]); + } else if(!strcmp(attribute_names[i], "protocol")) { + blist_parser_account_protocol = atoi(attribute_values[i]); + } + } + } else if(!strcmp(element_name, "name")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_NAME)); + } else if(!strcmp(element_name, "alias")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_ALIAS)); + } else if(!strcmp(element_name, "setting")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_SETTING)); + for(i=0; attribute_names[i]; i++) { + if(!strcmp(attribute_names[i], "name")) { + g_free(blist_parser_setting_name); + blist_parser_setting_name = g_strdup(attribute_values[i]); + } + } + } else if(!strcmp(element_name, "privacy")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PRIVACY)); + } else if(!strcmp(element_name, "account")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_ACCOUNT)); + for(i=0; attribute_names[i]; i++) { + if(!strcmp(attribute_names[i], "protocol")) + blist_parser_account_protocol = atoi(attribute_values[i]); + else if(!strcmp(attribute_names[i], "mode")) + blist_parser_privacy_mode = atoi(attribute_values[i]); + else if(!strcmp(attribute_names[i], "name")) { + g_free(blist_parser_account_name); + blist_parser_account_name = g_strdup(attribute_values[i]); + } + } + } else if(!strcmp(element_name, "permit")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PERMIT)); + } else if(!strcmp(element_name, "block")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BLOCK)); + } else if(!strcmp(element_name, "ignore")) { + tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_IGNORE)); + } +} + +static void blist_end_element_handler(GMarkupParseContext *context, + const gchar *element_name, gpointer user_data, GError **error) { + if(!strcmp(element_name, "gaim")) { + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } else if(!strcmp(element_name, "blist")) { + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } else if(!strcmp(element_name, "group")) { + if(blist_parser_group_settings) { + struct group *g = gaim_find_group(blist_parser_group_name); + g_hash_table_destroy(g->settings); + g->settings = blist_parser_group_settings; + } + tag_stack = g_list_delete_link(tag_stack, tag_stack); + blist_parser_group_settings = NULL; + } else if(!strcmp(element_name, "person")) { + g_free(blist_parser_person_name); + blist_parser_person_name = NULL; + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } else if(!strcmp(element_name, "buddy")) { + struct gaim_account *account = gaim_account_find(blist_parser_account_name, + blist_parser_account_protocol); + if(account) { + struct buddy *b = gaim_buddy_new(account, blist_parser_buddy_name, blist_parser_buddy_alias); + struct group *g = gaim_find_group(blist_parser_group_name); + gaim_blist_add_buddy(b,g,NULL); + if(blist_parser_buddy_settings) { + g_hash_table_destroy(b->settings); + b->settings = blist_parser_buddy_settings; + } + } + g_free(blist_parser_buddy_name); + blist_parser_buddy_name = NULL; + g_free(blist_parser_buddy_alias); + blist_parser_buddy_alias = NULL; + g_free(blist_parser_account_name); + blist_parser_account_name = NULL; + blist_parser_buddy_settings = NULL; + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } else if(!strcmp(element_name, "name")) { + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } else if(!strcmp(element_name, "alias")) { + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } else if(!strcmp(element_name, "setting")) { + if(GPOINTER_TO_INT(tag_stack->next->data) == BLIST_TAG_BUDDY) { + if(!blist_parser_buddy_settings) + blist_parser_buddy_settings = g_hash_table_new_full(g_str_hash, + g_str_equal, g_free, g_free); + if(blist_parser_setting_name && blist_parser_setting_value) { + g_hash_table_replace(blist_parser_buddy_settings, + g_strdup(blist_parser_setting_name), + g_strdup(blist_parser_setting_value)); + } + } else if(GPOINTER_TO_INT(tag_stack->next->data) == BLIST_TAG_GROUP) { + if(!blist_parser_group_settings) + blist_parser_group_settings = g_hash_table_new_full(g_str_hash, + g_str_equal, g_free, g_free); + if(blist_parser_setting_name && blist_parser_setting_value) { + g_hash_table_replace(blist_parser_group_settings, + g_strdup(blist_parser_setting_name), + g_strdup(blist_parser_setting_value)); + } + } + g_free(blist_parser_setting_name); + g_free(blist_parser_setting_value); + blist_parser_setting_name = NULL; + blist_parser_setting_value = NULL; + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } else if(!strcmp(element_name, "privacy")) { + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } else if(!strcmp(element_name, "account")) { + struct gaim_account *account = gaim_account_find(blist_parser_account_name, + blist_parser_account_protocol); + if(account) { + account->permdeny = blist_parser_privacy_mode; + } + g_free(blist_parser_account_name); + blist_parser_account_name = NULL; + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } else if(!strcmp(element_name, "permit")) { + struct gaim_account *account = gaim_account_find(blist_parser_account_name, + blist_parser_account_protocol); + if(account) { + gaim_privacy_permit_add(account, blist_parser_buddy_name); + } + g_free(blist_parser_buddy_name); + blist_parser_buddy_name = NULL; + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } else if(!strcmp(element_name, "block")) { + struct gaim_account *account = gaim_account_find(blist_parser_account_name, + blist_parser_account_protocol); + if(account) { + gaim_privacy_deny_add(account, blist_parser_buddy_name); + } + g_free(blist_parser_buddy_name); + blist_parser_buddy_name = NULL; + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } else if(!strcmp(element_name, "ignore")) { + /* we'll apparently do something with this later */ + tag_stack = g_list_delete_link(tag_stack, tag_stack); + } +} + +static void blist_text_handler(GMarkupParseContext *context, const gchar *text, + gsize text_len, gpointer user_data, GError **error) { + switch(GPOINTER_TO_INT(tag_stack->data)) { + case BLIST_TAG_NAME: + blist_parser_buddy_name = g_strndup(text, text_len); + break; + case BLIST_TAG_ALIAS: + blist_parser_buddy_alias = g_strndup(text, text_len); + break; + case BLIST_TAG_PERMIT: + case BLIST_TAG_BLOCK: + case BLIST_TAG_IGNORE: + blist_parser_buddy_name = g_strndup(text, text_len); + break; + case BLIST_TAG_SETTING: + blist_parser_setting_value = g_strndup(text, text_len); + break; + default: + break; + } +} + +static void blist_error_handler(GMarkupParseContext *context, GError *error, + gpointer user_data) { + blist_parser_error_occurred = TRUE; + gaim_debug(GAIM_DEBUG_ERROR, "blist import", + "Error parsing blist.xml: %s\n", error->message); +} + +static GMarkupParser blist_parser = { + blist_start_element_handler, + blist_end_element_handler, + blist_text_handler, + NULL, + blist_error_handler +}; + +static gboolean gaim_blist_read(const char *filename) { + gchar *contents = NULL; + gsize length; + GMarkupParseContext *context; + GError *error = NULL; + + gaim_debug(GAIM_DEBUG_INFO, "blist import", + "Reading %s\n", filename); + if(!g_file_get_contents(filename, &contents, &length, &error)) { + gaim_debug(GAIM_DEBUG_ERROR, "blist import", + "Error reading blist: %s\n", error->message); + g_error_free(error); + return FALSE; + } + + context = g_markup_parse_context_new(&blist_parser, 0, NULL, NULL); + + if(!g_markup_parse_context_parse(context, contents, length, NULL)) { + g_markup_parse_context_free(context); + g_free(contents); + return FALSE; + } + + if(!g_markup_parse_context_end_parse(context, NULL)) { + gaim_debug(GAIM_DEBUG_ERROR, "blist import", + "Error parsing %s\n", filename); + g_markup_parse_context_free(context); + g_free(contents); + return FALSE; + } + + g_markup_parse_context_free(context); + g_free(contents); + + if(blist_parser_error_occurred) + return FALSE; + + gaim_debug(GAIM_DEBUG_INFO, "blist import", "Finished reading %s\n", + filename); + + return TRUE; +} + +void gaim_blist_load() { + GSList *accts; + char *user_dir = gaim_user_dir(); + char *filename; + char *msg; + + blist_safe_to_write = TRUE; + + if(!user_dir) + return; + + filename = g_build_filename(user_dir, "blist.xml", NULL); + + if(g_file_test(filename, G_FILE_TEST_EXISTS)) { + if(!gaim_blist_read(filename)) { + msg = g_strdup_printf(_("An error was encountered parsing your " + "buddy list. It has not been loaded.")); + do_error_dialog(_("Buddy List Error"), msg, GAIM_ERROR); + g_free(msg); + } + } else if(g_slist_length(gaim_accounts)) { + /* rob wants to inform the user that their buddy lists are + * being converted */ + msg = g_strdup_printf(_("Gaim is converting your old buddy lists " + "to a new format, which will now be located at %s"), + filename); + do_error_dialog(_("Converting Buddy List"), msg, GAIM_INFO); + g_free(msg); + + /* now, let gtk actually display the dialog before we start anything */ + while(gtk_events_pending()) + gtk_main_iteration(); + + /* read in the old lists, then save to the new format */ + for(accts = gaim_accounts; accts; accts = accts->next) { + do_import(accts->data, NULL); + } + gaim_blist_save(); + } + + g_free(filename); +} + +static void blist_print_group_settings(gpointer key, gpointer data, + gpointer user_data) { + char *key_val; + char *data_val; + FILE *file = user_data; + + if(!key || !data) + return; + + key_val = g_markup_escape_text(key, -1); + data_val = g_markup_escape_text(data, -1); + + fprintf(file, "\t\t\t%s\n", key_val, + data_val); + g_free(key_val); + g_free(data_val); +} + +static void blist_print_buddy_settings(gpointer key, gpointer data, + gpointer user_data) { + char *key_val; + char *data_val; + FILE *file = user_data; + + if(!key || !data) + return; + + key_val = g_markup_escape_text(key, -1); + data_val = g_markup_escape_text(data, -1); + + fprintf(file, "\t\t\t\t\t%s\n", key_val, + data_val); + g_free(key_val); + g_free(data_val); +} + +static void gaim_blist_write(FILE *file, struct gaim_account *exp_acct) { + GSList *accounts, *buds; + GaimBlistNode *gnode,*bnode; + struct group *group; + struct buddy *bud; + fprintf(file, "\n"); + fprintf(file, "\n"); + fprintf(file, "\t\n"); + + for(gnode = gaimbuddylist->root; gnode; gnode = gnode->next) { + if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) + continue; + group = (struct group *)gnode; + if(!exp_acct || gaim_group_on_account(group, exp_acct)) { + char *group_name = g_markup_escape_text(group->name, -1); + fprintf(file, "\t\t\n", group_name); + g_hash_table_foreach(group->settings, blist_print_group_settings, file); + for(bnode = gnode->child; bnode; bnode = bnode->next) { + if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) + continue; + bud = (struct buddy *)bnode; + if(!exp_acct || bud->account == exp_acct) { + char *bud_name = g_markup_escape_text(bud->name, -1); + char *bud_alias = NULL; + char *acct_name = g_markup_escape_text(bud->account->username, -1); + if(bud->alias) + bud_alias= g_markup_escape_text(bud->alias, -1); + fprintf(file, "\t\t\t\n", + bud_alias ? bud_alias : bud_name); + fprintf(file, "\t\t\t\t\n", bud->account->protocol, + acct_name); + fprintf(file, "\t\t\t\t\t%s\n", bud_name); + if(bud_alias) { + fprintf(file, "\t\t\t\t\t%s\n", + bud_alias); + } + g_hash_table_foreach(bud->settings, + blist_print_buddy_settings, file); + fprintf(file, "\t\t\t\t\n"); + fprintf(file, "\t\t\t\n"); + g_free(bud_name); + g_free(bud_alias); + g_free(acct_name); + } + } + fprintf(file, "\t\t\n"); + g_free(group_name); + } + } + + fprintf(file, "\t\n"); + fprintf(file, "\t\n"); + + for(accounts = gaim_accounts; accounts; accounts = accounts->next) { + struct gaim_account *account = accounts->data; + char *acct_name = g_markup_escape_text(account->username, -1); + if(!exp_acct || account == exp_acct) { + fprintf(file, "\t\t\n", account->protocol, acct_name, account->permdeny); + for(buds = account->permit; buds; buds = buds->next) { + char *bud_name = g_markup_escape_text(buds->data, -1); + fprintf(file, "\t\t\t%s\n", bud_name); + g_free(bud_name); + } + for(buds = account->deny; buds; buds = buds->next) { + char *bud_name = g_markup_escape_text(buds->data, -1); + fprintf(file, "\t\t\t%s\n", bud_name); + g_free(bud_name); + } + fprintf(file, "\t\t\n"); + } + g_free(acct_name); + } + + fprintf(file, "\t\n"); + fprintf(file, "\n"); +} + +void gaim_blist_save() { + FILE *file; + char *user_dir = gaim_user_dir(); + char *filename; + char *filename_real; + + if(!user_dir) + return; + if(!blist_safe_to_write) { + gaim_debug(GAIM_DEBUG_WARNING, "blist save", + "AHH!! Tried to write the blist before we read it!\n"); + return; + } + + file = fopen(user_dir, "r"); + if(!file) + mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR); + else + fclose(file); + + filename = g_build_filename(user_dir, "blist.xml.save", NULL); + + if((file = fopen(filename, "w"))) { + gaim_blist_write(file, NULL); + fclose(file); + chmod(filename, S_IRUSR | S_IWUSR); + } else { + gaim_debug(GAIM_DEBUG_ERROR, "blist save", "Unable to write %s\n", + filename); + } + + filename_real = g_build_filename(user_dir, "blist.xml", NULL); + + if(rename(filename, filename_real) < 0) + gaim_debug(GAIM_DEBUG_ERROR, "blist save", + "Error renaming %s to %s\n", filename, filename_real); + + + g_free(filename); + g_free(filename_real); +} + +gboolean gaim_privacy_permit_add(struct gaim_account *account, const char *who) { + GSList *d = account->permit; + char *n = g_strdup(normalize(who)); + while(d) { + if(!gaim_utf8_strcasecmp(n, normalize(d->data))) + break; + d = d->next; + } + g_free(n); + if(!d) { + account->permit = g_slist_append(account->permit, g_strdup(who)); + return TRUE; + } + + return FALSE; +} + +gboolean gaim_privacy_permit_remove(struct gaim_account *account, const char *who) { + GSList *d = account->permit; + char *n = g_strdup(normalize(who)); + while(d) { + if(!gaim_utf8_strcasecmp(n, normalize(d->data))) + break; + d = d->next; + } + g_free(n); + if(d) { + account->permit = g_slist_remove(account->permit, d->data); + g_free(d->data); + return TRUE; + } + return FALSE; +} + +gboolean gaim_privacy_deny_add(struct gaim_account *account, const char *who) { + GSList *d = account->deny; + char *n = g_strdup(normalize(who)); + while(d) { + if(!gaim_utf8_strcasecmp(n, normalize(d->data))) + break; + d = d->next; + } + g_free(n); + if(!d) { + account->deny = g_slist_append(account->deny, g_strdup(who)); + return TRUE; + } + + return FALSE; +} + +gboolean gaim_privacy_deny_remove(struct gaim_account *account, const char *who) { + GSList *d = account->deny; + char *n = g_strdup(normalize(who)); + while(d) { + if(!gaim_utf8_strcasecmp(n, normalize(d->data))) + break; + d = d->next; + } + g_free(n); + if(d) { + account->deny = g_slist_remove(account->deny, d->data); + g_free(d->data); + return TRUE; + } + return FALSE; +} + +void gaim_group_set_setting(struct group *g, const char *key, + const char *value) { + if(!g) + return; + g_hash_table_replace(g->settings, g_strdup(key), g_strdup(value)); +} + +char *gaim_group_get_setting(struct group *g, const char *key) { + if(!g) + return NULL; + return g_strdup(g_hash_table_lookup(g->settings, key)); +} + +void gaim_buddy_set_setting(struct buddy *b, const char *key, + const char *value) { + if(!b) + return; + g_hash_table_replace(b->settings, g_strdup(key), g_strdup(value)); +} + +char *gaim_buddy_get_setting(struct buddy *b, const char *key) { + if(!b) + return NULL; + return g_strdup(g_hash_table_lookup(b->settings, key)); +} + +void gaim_set_blist_ui_ops(struct gaim_blist_ui_ops *ops) +{ + blist_ui_ops = ops; +} + +struct gaim_blist_ui_ops * +gaim_get_blist_ui_ops(void) +{ + return blist_ui_ops; +} + +int gaim_blist_get_group_size(struct group *group, gboolean offline) { + GaimBlistNode *node; + int count = 0; + + if(!group) + return 0; + + for(node = group->node.child; node; node = node->next) { + if(GAIM_BLIST_NODE_IS_BUDDY(node)) { + struct buddy *b = (struct buddy *)node; + if(b->account->gc || offline) + count++; + } + } + + return count; +} + +int gaim_blist_get_group_online_count(struct group *group) { + GaimBlistNode *node; + int count = 0; + + if(!group) + return 0; + + for(node = group->node.child; node; node = node->next) { + if(GAIM_BLIST_NODE_IS_BUDDY(node)) { + struct buddy *b = (struct buddy *)node; + if(GAIM_BUDDY_IS_ONLINE(b)) + count++; + } + } + + return count; +} + + diff -r 6d1707dc8c3d -r 1a53330dfd34 src/blist.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/blist.h Sat Apr 26 17:56:23 2003 +0000 @@ -0,0 +1,479 @@ +/** + * @file blist.h Buddy List API + * @ingroup core + * + * gaim + * + * Copyright (C) 2003, Sean Egan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* I can't believe I let ChipX86 inspire me to write good code. -Sean */ + +#ifndef _LIST_H_ +#define _LIST_H_ + +#include + +/**************************************************************************/ +/* Enumerations */ +/**************************************************************************/ +enum gaim_blist_node_type { + GAIM_BLIST_GROUP_NODE, + GAIM_BLIST_BUDDY_NODE, + GAIM_BLIST_OTHER_NODE, +}; + +#define GAIM_BLIST_NODE_IS_BUDDY(n) ((n)->type == GAIM_BLIST_BUDDY_NODE) +#define GAIM_BLIST_NODE_IS_GROUP(n) ((n)->type == GAIM_BLIST_GROUP_NODE) + +enum gaim_buddy_presence_state { + GAIM_BUDDY_SIGNING_OFF = -1, + GAIM_BUDDY_OFFLINE = 0, + GAIM_BUDDY_ONLINE, + GAIM_BUDDY_SIGNING_ON, +}; + +#define GAIM_BUDDY_IS_ONLINE(b) ((b)->present == GAIM_BUDDY_ONLINE || \ + (b)->present == GAIM_BUDDY_SIGNING_ON) + + +/**************************************************************************/ +/* Data Structures */ +/**************************************************************************/ + +typedef struct _GaimBlistNode GaimBlistNode; +/** + * A Buddy list node. This can represent a group, a buddy, or anything else. This is a base class for struct buddy and + * struct group and for anything else that wants to put itself in the buddy list. */ +struct _GaimBlistNode { + enum gaim_blist_node_type type; /**< The type of node this is */ + GaimBlistNode *prev; /**< The sibling before this buddy. */ + GaimBlistNode *next; /**< The sibling after this buddy. */ + GaimBlistNode *parent; /**< The parent of this node */ + GaimBlistNode *child; /**< The child of this node */ + void *ui_data; /**< The UI can put data here. */ +}; + +/** + * A buddy. This contains everything Gaim will ever need to know about someone on the buddy list. Everything. + */ +struct buddy { + GaimBlistNode node; /**< The node that this buddy inherits from */ + char *name; /**< The screenname of the buddy. */ + char *alias; /**< The user-set alias of the buddy */ + char *server_alias; /**< The server-specified alias of the buddy. (i.e. MSN "Friendly Names") */ + enum gaim_buddy_presence_state present; /**< This is 0 if the buddy appears offline, 1 if he appears online, and 2 if + he has recently signed on */ + int evil; /**< The warning level */ + time_t signon; /**< The time the buddy signed on. */ + int idle; /**< The time the buddy has been idle in minutes. */ + int uc; /**< This is a cryptic bitmask that makes sense only to the prpl. This will get changed */ + void *proto_data; /**< This allows the prpl to associate whatever data it wants with a buddy */ + struct gaim_account *account; /**< the account this buddy belongs to */ + GHashTable *settings; /**< per-buddy settings from the XML buddy list, set by plugins and the likes. */ + guint timer; /**< The timer handle. */ +}; + +/** + * A group. This contains everything Gaim will ever need to know about a group. + */ +struct group { + GaimBlistNode node; /**< The node that this group inherits from */ + char *name; /**< The name of this group. */ + GHashTable *settings; /**< per-group settings from the XML buddy list, set by plugins and the likes. */ +}; + + +/** + * The Buddy List + */ +struct gaim_buddy_list { + GaimBlistNode *root; /**< The first node in the buddy list */ + struct gaim_blist_ui_ops *ui_ops; /**< The UI operations for the buddy list */ + + void *ui_data; /**< UI-specific data. */ +}; + +/** + * Buddy list UI operations. + * + * Any UI representing a buddy list must assign a filled-out gaim_window_ops + * structure to the buddy list core. + */ +struct gaim_blist_ui_ops +{ + void (*new_list)(struct gaim_buddy_list *list); /**< Sets UI-specific data on a buddy list. */ + void (*new_node)(GaimBlistNode *node); /**< Sets UI-specific data on a node. */ + void (*show)(struct gaim_buddy_list *list); /**< The core will call this when its finished doing it's core stuff */ + void (*update)(struct gaim_buddy_list *list, + GaimBlistNode *node); /**< This will update a node in the buddy list. */ + void (*remove)(struct gaim_buddy_list *list, + GaimBlistNode *node); /**< This removes a node from the list */ + void (*destroy)(struct gaim_buddy_list *list); /**< When the list gets destroyed, this gets called to destroy the UI. */ + void (*set_visible)(struct gaim_buddy_list *list, + gboolean show); /**< Hides or unhides the buddy list */ + +}; + +/**************************************************************************/ +/** @name Buddy List API */ +/**************************************************************************/ +/*@{*/ + +/** + * Creates a new buddy list + */ +struct gaim_buddy_list *gaim_blist_new(); + +/** + * Sets the main buddy list. + * + * @return The main buddy list. + */ +void gaim_set_blist(struct gaim_buddy_list *blist); + +/** + * Returns the main buddy list. + * + * @return The main buddy list. + */ +struct gaim_buddy_list *gaim_get_blist(void); + +/** + * Shows the buddy list, creating a new one if necessary. + * + */ +void gaim_blist_show(); + + +/** + * Destroys the buddy list window. + */ +void gaim_blist_destroy(); + +/** + * Hides or unhides the buddy list. + * + * @param show Whether or not to show the buddy list + */ +void gaim_blist_set_visible(gboolean show); + +/** + * Updates a buddy's status. + * + * This needs to not take an int. + * + * @param buddy The buddy whose status has changed + * @param status The new status in cryptic prpl-understood code + */ +void gaim_blist_update_buddy_status(struct buddy *buddy, int status); + + +/** + * Updates a buddy's presence. + * + * @param buddy The buddy whose presence has changed + * @param presence The new presence + */ +void gaim_blist_update_buddy_presence(struct buddy *buddy, int presence); + + +/** + * Updates a buddy's idle time. + * + * @param buddy The buddy whose idle time has changed + * @param idle The buddy's idle time in minutes. + */ +void gaim_blist_update_buddy_idle(struct buddy *buddy, int idle); + + +/** + * Updates a buddy's warning level. + * + * @param buddy The buddy whose warning level has changed + * @param evil The warning level as an int from 0 to 100 (or higher, I guess... but that'd be weird) + */ +void gaim_blist_update_buddy_evil(struct buddy *buddy, int warning); + +/** + * Updates a buddy's icon. + * + * @param buddy The buddy whose buddy icon has changed + */ +void gaim_blist_update_buddy_icon(struct buddy *buddy); + + + +/** + * Renames a buddy in the buddy list. + * + * @param buddy The buddy whose name will be changed. + * @param name The new name of the buddy. + */ +void gaim_blist_rename_buddy(struct buddy *buddy, const char *name); + + +/** + * Aliases a buddy in the buddy list. + * + * @param buddy The buddy whose alias will be changed. + * @param alias The buddy's alias. + */ +void gaim_blist_alias_buddy(struct buddy *buddy, const char *alias); + + +/** + * Renames a group + * + * @param group The group to rename + * @param name The new name + */ +void gaim_blist_rename_group(struct group *group, const char *name); + + +/** + * Creates a new buddy + * + * @param account The account this buddy will get added to + * @param screenname The screenname of the new buddy + * @param alias The alias of the new buddy (or NULL if unaliased) + * @return A newly allocated buddy + */ +struct buddy *gaim_buddy_new(struct gaim_account *account, const char *screenname, const char *alias); + +/** + * Adds a new buddy to the buddy list. + * + * The buddy will be inserted right after node or appended to the end + * of group if node is NULL. If both are NULL, the buddy will be added to + * the "Buddies" group. + * + * @param buddy The new buddy who gets added + * @param group The group to add the new buddy to. + * @param node The insertion point + */ +void gaim_blist_add_buddy(struct buddy *buddy, struct group *group, GaimBlistNode *node); + +/** + * Creates a new group + * + * You can't have more than one group with the same name. Sorry. If you pass this the + * name of a group that already exists, it will return that group. + * + * @param name The name of the new group + * @return A new group struct +*/ +struct group *gaim_group_new(const char *name); + +/** + * Adds a new group to the buddy list. + * + * The new group will be inserted after insert or appended to the end of + * the list if node is NULL. + * + * @param group The group to add the new buddy to. + * @param node The insertion point + */ +void gaim_blist_add_group(struct group *group, GaimBlistNode *node); + +/** + * Removes a buddy from the buddy list and frees the memory allocated to it. + * + * @param buddy The buddy to be removed + */ +void gaim_blist_remove_buddy(struct buddy *buddy); + +/** + * Removes a group from the buddy list and frees the memory allocated to it and to + * its children + * + * @param group The group to be removed + */ +void gaim_blist_remove_group(struct group *group); + +/** + * Returns the alias of a buddy. + * + * @param buddy The buddy whose name will be returned. + * @return The alias (if set), server alias (if option is set), or NULL. + */ +char *gaim_get_buddy_alias_only(struct buddy *buddy); + + +/** + * Returns the correct name to display for a buddy. + * + * @param buddy The buddy whose name will be returned. + * @return The alias (if set), server alias (if option is set), screenname, or "Unknown" + */ +char *gaim_get_buddy_alias(struct buddy *buddy); + +/** + * Finds the buddy struct given a screenname and an account + * + * @param name The buddy's screenname + * @param account The account this buddy belongs to + * @return The buddy or NULL if the buddy does not exist + */ +struct buddy *gaim_find_buddy(struct gaim_account *account, const char *name); + +/** + * Finds a group by name + * + * @param name The groups name + * @return The group or NULL if the group does not exist + */ +struct group *gaim_find_group(const char *name); + +/** + * Returns the group of which the buddy is a member. + * + * @param buddy The buddy + * @return The group or NULL if the buddy is not in a group + */ +struct group *gaim_find_buddys_group(struct buddy *buddy); + + +/** + * Returns a list of accounts that have buddies in this group + * + * @param group The group + * @return A list of gaim_accounts + */ +GSList *gaim_group_get_accounts(struct group *g); + +/** + * Determines whether an account owns any buddies in a given group + * + * @param g The group to search through. + * @param account The account. + */ +gboolean gaim_group_on_account(struct group *g, struct gaim_account *account); + +/** + * Called when an account gets signed off. Sets the presence of all the buddies to 0 + * and tells the UI to update them. + * + * @param account The account + */ +void gaim_blist_remove_account(struct gaim_account *account); + + +/** + * Determines the total size of a group + * + * @param group The group + * @param offline Count buddies in offline accounts + * @return The number of buddies in the group + */ +int gaim_blist_get_group_size(struct group *group, gboolean offline); + +/** + * Determines the number of online buddies in a group + * + * @param group The group + * @return The number of online buddies in the group, or 0 if the group is NULL + */ +int gaim_blist_get_group_online_count(struct group *group); + +/*@}*/ + +/****************************************************************************************/ +/** @name Buddy list file management API */ +/****************************************************************************************/ + +/*@{*/ +/** + * Saves the buddy list to file + */ +void gaim_blist_save(); + +/** + * Parses the toc-style buddy list used in older versions of Gaim and for SSI in toc.c + * + * @param account This is the account that the buddies and groups from config will get added to + * @param config This is the toc-style buddy list data + */ +void parse_toc_buddy_list(struct gaim_account *account, char *config); + + +/** + * Loads the buddy list from ~/.gaim/blist.xml. + */ +void gaim_blist_load(); + +/** + * Associates some data with the group in the xml buddy list + * + * @param g The group the data is associated with + * @param key The key used to retrieve the data + * @param value The data to set + */ +void gaim_group_set_setting(struct group *g, const char *key, const char *value); + +/** + * Retrieves data from the XML buddy list set by gaim_group_set_setting()) + * + * @param g The group to retrieve data from + * @param key The key to retrieve the data with + * @return The associated data or NULL if no data is associated + */ +char *gaim_group_get_setting(struct group *g, const char *key); + + +/** + * Associates some data with the buddy in the xml buddy list + * + * @param b The buddy the data is associated with + * @param key The key used to retrieve the data + * @param value The data to set + */ +void gaim_buddy_set_setting(struct buddy *b, const char *key, const char *value); + +/** + * Retrieves data from the XML buddy list set by gaim_buddy_set_setting()) + * + * @param b The buddy to retrieve data from + * @param key The key to retrieve the data with + * @return The associated data or NULL if no data is associated + */ +char *gaim_buddy_get_setting(struct buddy *b, const char *key); + +/*@}*/ + +/**************************************************************************/ +/** @name UI Registration Functions */ +/**************************************************************************/ +/*@{*/ + +/** + * Sets the UI operations structure to be used for the buddy list. + * + * @param ops The ops struct. + */ +void gaim_set_blist_ui_ops(struct gaim_blist_ui_ops *ops); + +/** + * Returns the UI operations structure to be used for the buddy list. + * + * @return The UI operations structure. + */ +struct gaim_blist_ui_ops *gaim_get_blist_ui_ops(void); + +/*@}*/ + +#endif /* _LIST_H_ */ diff -r 6d1707dc8c3d -r 1a53330dfd34 src/buddy.c --- a/src/buddy.c Sat Apr 26 17:36:52 2003 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1824 +0,0 @@ -/* - * gaim - * - * Copyright (C) 1998-1999, Mark Spencer - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif -#ifdef GAIM_PLUGINS -#ifndef _WIN32 -#include -#endif -#endif /* GAIM_PLUGINS */ -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#endif - -#include -#include -#include "prpl.h" -#include "sound.h" -#include "gaim.h" -#include "gtklist.h" -#include "gtkpounce.h" -#include "gtkft.h" - -#ifdef _WIN32 -#include "win32dep.h" -#endif - -static struct gaim_gtk_buddy_list *gtkblist = NULL; - -/* part of the best damn Docklet code this side of Tahiti */ -static gboolean gaim_gtk_blist_obscured = FALSE; - -static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data); -static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node); -static char *gaim_get_tooltip_text(struct buddy *b); -static char *item_factory_translate_func (const char *path, gpointer func_data); - -/*************************************************** - * Callbacks * - ***************************************************/ - -static gboolean gtk_blist_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data) -{ - if (docklet_count) - gaim_blist_set_visible(FALSE); - else - do_quit(); - - /* we handle everything, event should not propogate further */ - return TRUE; -} - -static gboolean gtk_blist_save_prefs_cb(gpointer data) -{ - save_prefs(); - - /* only run once */ - return FALSE; -} - -static gboolean gtk_blist_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data) -{ - /* unfortunately GdkEventConfigure ignores the window gravity, but * - * the only way we have of setting the position doesn't. we have to * - * call get_position and get_size because they do pay attention to * - * the gravity. this is inefficient and I agree it sucks, but it's * - * more likely to work correctly. - Robot101 */ - gint x, y; - - /* check for visibility because when we aren't visible, this will * - * give us bogus (0,0) coordinates. - xOr */ - if (GTK_WIDGET_VISIBLE(w)) { - gtk_window_get_position(GTK_WINDOW(w), &x, &y); - - if (x != blist_pos.x || - y != blist_pos.y || - event->width != blist_pos.width || - event->height != blist_pos.height) { - blist_pos.x = x; - blist_pos.y = y; - blist_pos.width = event->width; - blist_pos.height = event->height; - - if (!g_main_context_find_source_by_user_data(NULL, >k_blist_save_prefs_cb)) { - g_timeout_add(5000, gtk_blist_save_prefs_cb, >k_blist_save_prefs_cb); - } - } - } - - /* continue to handle event normally */ - return FALSE; -} - -static gboolean gtk_blist_visibility_cb(GtkWidget *w, GdkEventVisibility *event, gpointer data) -{ - if (event->state == GDK_VISIBILITY_FULLY_OBSCURED) - gaim_gtk_blist_obscured = TRUE; - else - gaim_gtk_blist_obscured = FALSE; - - /* continue to handle event normally */ - return FALSE; -} - -static void gtk_blist_menu_info_cb(GtkWidget *w, struct buddy *b) -{ - serv_get_info(b->account->gc, b->name); -} - -static void gtk_blist_menu_im_cb(GtkWidget *w, struct buddy *b) -{ - gaim_conversation_new(GAIM_CONV_IM, b->account, b->name); -} - -static void gtk_blist_menu_alias_cb(GtkWidget *w, struct buddy *b) -{ - alias_dialog_bud(b); -} - -static void gtk_blist_menu_bp_cb(GtkWidget *w, struct buddy *b) -{ - gaim_gtkpounce_dialog_show(b, NULL); -} - -static void gtk_blist_menu_showlog_cb(GtkWidget *w, struct buddy *b) -{ - show_log(b->name); -} - -static void gtk_blist_show_systemlog_cb() -{ - show_log(NULL); -} - -static void gtk_blist_show_onlinehelp_cb() -{ - open_url(NULL, WEBSITE "documentation.php"); -} - -static void gtk_blist_button_im_cb(GtkWidget *w, GtkTreeView *tv) -{ - GtkTreeIter iter; - GtkTreeModel *model = gtk_tree_view_get_model(tv); - GtkTreeSelection *sel = gtk_tree_view_get_selection(tv); - - if(gtk_tree_selection_get_selected(sel, &model, &iter)){ - GaimBlistNode *node; - - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); - if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name); - return; - } - } - show_im_dialog(); -} - -static void gtk_blist_button_info_cb(GtkWidget *w, GtkTreeView *tv) -{ - GtkTreeIter iter; - GtkTreeModel *model = gtk_tree_view_get_model(tv); - GtkTreeSelection *sel = gtk_tree_view_get_selection(tv); - - if(gtk_tree_selection_get_selected(sel, &model, &iter)){ - GaimBlistNode *node; - - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); - if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - serv_get_info(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name); - return; - } - } - show_info_dialog(); -} - -static void gtk_blist_button_chat_cb(GtkWidget *w, gpointer data) -{ - /* FIXME: someday, we can check to see if we've selected a chat node */ - join_chat(); -} - -static void gtk_blist_button_away_cb(GtkWidget *w, gpointer data) -{ - gtk_menu_popup(GTK_MENU(awaymenu), NULL, NULL, NULL, NULL, 1, GDK_CURRENT_TIME); -} - -static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) { - GaimBlistNode *node; - GValue val = {0,}; - - gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val); - - node = g_value_get_pointer(&val); - - if (GAIM_BLIST_NODE_IS_GROUP(node)) { - gaim_group_set_setting((struct group *)node, "collapsed", NULL); - gaim_blist_save(); - } -} - -static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) { - GaimBlistNode *node; - GValue val = {0,}; - - gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val); - - node = g_value_get_pointer(&val); - - if (GAIM_BLIST_NODE_IS_GROUP(node)) { - gaim_group_set_setting((struct group *)node, "collapsed", "true"); - gaim_blist_save(); - } -} - -static void gtk_blist_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) { - GaimBlistNode *node; - GtkTreeIter iter; - GValue val = { 0, }; - - gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - - gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); - node = g_value_get_pointer(&val); - - if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - struct gaim_conversation *conv = - gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name); - if(conv) { - gaim_window_raise(gaim_conversation_get_window(conv)); - gaim_window_switch_conversation( - gaim_conversation_get_window(conv), - gaim_conversation_get_index(conv)); - } - } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { - if (gtk_tree_view_row_expanded(tv, path)) - gtk_tree_view_collapse_row(tv, path); - else - gtk_tree_view_expand_row(tv,path,FALSE); - } -} - -static void gaim_gtk_blist_add_buddy_cb() -{ - GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); - GtkTreeIter iter; - GaimBlistNode *node; - - if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); - if (GAIM_BLIST_NODE_IS_BUDDY(node)) - show_add_buddy(NULL, NULL, ((struct group*)node->parent)->name, NULL); - else if (GAIM_BLIST_NODE_IS_GROUP(node)) - show_add_buddy(NULL, NULL, ((struct group*)node)->name, NULL); - } - else { - show_add_buddy(NULL, NULL, NULL, NULL); - } -} - -static void -gaim_gtk_blist_remove_cb (GtkWidget *w, GaimBlistNode *node) -{ - if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - struct buddy *b = (struct buddy*)node; - show_confirm_del(b->account->gc, b->name); - } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { - struct group *g = (struct group*)node; - show_confirm_del_group(g); - } -} - -static void gaim_proto_menu_cb(GtkMenuItem *item, struct buddy *b) -{ - struct proto_buddy_menu *pbm = g_object_get_data(G_OBJECT(item), "gaimcallback"); - if (pbm->callback) - pbm->callback(pbm->gc, b->name); -} - -static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer null) -{ - GtkTreePath *path; - GaimBlistNode *node; - GValue val = { 0, }; - GtkTreeIter iter; - GtkWidget *menu, *menuitem; - GtkTreeSelection *sel; - GList *list; - GaimPlugin *prpl = NULL; - GaimPluginProtocolInfo *prpl_info = NULL; - - if (event->button != 3) - return FALSE; - - /* Here we figure out which node was clicked */ - if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL)) - return FALSE; - gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); - node = g_value_get_pointer(&val); - menu = gtk_menu_new(); - - if (GAIM_BLIST_NODE_IS_GROUP(node)) { - gaim_new_item_from_stock(menu, _("_Add a Buddy"), GTK_STOCK_ADD, G_CALLBACK(gaim_gtk_blist_add_buddy_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Rename"), NULL, G_CALLBACK(show_rename_group), node, 0, 0, NULL); - } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - /* Protocol specific options */ - prpl = gaim_find_prpl(((struct buddy*)node)->account->protocol); - - if (prpl != NULL) - prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl && prpl_info->get_info) - gaim_new_item_from_stock(menu, _("_Get Info"), GAIM_STOCK_INFO, G_CALLBACK(gtk_blist_menu_info_cb), node, 0, 0, NULL); - - gaim_new_item_from_stock(menu, _("_IM"), GAIM_STOCK_IM, G_CALLBACK(gtk_blist_menu_im_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL, G_CALLBACK(gtk_blist_menu_bp_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("View _Log"), NULL, G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL); - - if (prpl) { - list = prpl_info->buddy_menu(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name); - while (list) { - struct proto_buddy_menu *pbm = list->data; - menuitem = gtk_menu_item_new_with_mnemonic(pbm->label); - g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm); - g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_proto_menu_cb), node); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); - list = list->next; - } - } - - gaim_event_broadcast (event_draw_menu, menu, ((struct buddy *) node)->name); - - gaim_separator(menu); - gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); - gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); - } - - gtk_widget_show_all(menu); - - gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); - -#if (1) /* This code only exists because GTK doesn't work. If we return FALSE here, as would be normal - * the event propoagates down and somehow gets interpreted as the start of a drag event. */ - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); - gtk_tree_selection_select_path(sel, path); - gtk_tree_path_free(path); - return TRUE; -#endif -} - -static void gaim_gtk_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item) -{ - if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) - blist_options &= ~OPT_BLIST_NO_MT_GRP; - else - blist_options |= OPT_BLIST_NO_MT_GRP; - save_prefs(); - gaim_gtk_blist_refresh(gaim_get_blist()); -} - -static void gaim_gtk_blist_edit_mode_cb(gpointer callback_data, guint callback_action, - GtkWidget *checkitem) { - if(gtkblist->window->window) { - GdkCursor *cursor = gdk_cursor_new(GDK_WATCH); - gdk_window_set_cursor(gtkblist->window->window, cursor); - while (gtk_events_pending()) - gtk_main_iteration(); - gdk_cursor_unref(cursor); - } - - if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(checkitem))) - blist_options |= OPT_BLIST_SHOW_OFFLINE; - else - blist_options &= ~OPT_BLIST_SHOW_OFFLINE; - save_prefs(); - - if(gtkblist->window->window) { - GdkCursor *cursor = gdk_cursor_new(GDK_LEFT_PTR); - gdk_window_set_cursor(gtkblist->window->window, cursor); - gdk_cursor_unref(cursor); - } - - gaim_gtk_blist_refresh(gaim_get_blist()); -} - -static void gaim_gtk_blist_drag_data_get_cb (GtkWidget *widget, - GdkDragContext *dc, - GtkSelectionData *data, - guint info, - guint time, - gpointer *null) -{ - if (data->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE)) { - GtkTreeRowReference *ref = g_object_get_data(G_OBJECT(dc), "gtk-tree-view-source-row"); - GtkTreePath *sourcerow = gtk_tree_row_reference_get_path(ref); - GtkTreeIter iter; - GaimBlistNode *node = NULL; - GValue val = {0}; - gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, sourcerow); - gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); - node = g_value_get_pointer(&val); - gtk_selection_data_set (data, - gdk_atom_intern ("GAIM_BLIST_NODE", FALSE), - 8, /* bits */ - (void*)&node, - sizeof (node)); - - gtk_tree_path_free(sourcerow); - } - -} - -static void gaim_gtk_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, - GtkSelectionData *sd, guint info, guint t) -{ - if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE) && sd->data) { - GaimBlistNode *n = NULL; - GtkTreePath *path = NULL; - GtkTreeViewDropPosition position; - memcpy(&n, sd->data, sizeof(n)); - if(gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, &path, &position)) { - /* if we're here, I think it means the drop is ok */ - GtkTreeIter iter; - GaimBlistNode *node; - GValue val = {0}; - gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); - node = g_value_get_pointer(&val); - - if (GAIM_BLIST_NODE_IS_BUDDY(n)) { - struct buddy *b = (struct buddy*)n; - if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - switch(position) { - case GTK_TREE_VIEW_DROP_AFTER: - case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: - gaim_blist_add_buddy(b, (struct group*)node->parent, node); - break; - case GTK_TREE_VIEW_DROP_BEFORE: - case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: - gaim_blist_add_buddy(b, (struct group*)node->parent, node->prev); - break; - } - } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { - gaim_blist_add_buddy(b, (struct group*)node, NULL); - } - } else if (GAIM_BLIST_NODE_IS_GROUP(n)) { - struct group *g = (struct group*)n; - if (GAIM_BLIST_NODE_IS_GROUP(node)) { - switch (position) { - case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: - case GTK_TREE_VIEW_DROP_AFTER: - gaim_blist_add_group(g, node); - break; - case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: - case GTK_TREE_VIEW_DROP_BEFORE: - gaim_blist_add_group(g, node->prev); - break; - } - - } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { - gaim_blist_add_group(g, node->parent); - } - - } - - gtk_tree_path_free(path); - gaim_blist_save(); - } - } -} - -static void gaim_gtk_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, struct buddy *b) -{ - GtkStyle *style; - GdkPixbuf *pixbuf = gaim_gtk_blist_get_status_icon(b, GAIM_STATUS_ICON_LARGE); - PangoLayout *layout; - char *tooltiptext = gaim_get_tooltip_text(b); - - layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); - pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); - pango_layout_set_wrap(layout, PANGO_WRAP_WORD); - pango_layout_set_width(layout, 300000); - style = gtkblist->tipwindow->style; - - gtk_paint_flat_box (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, - NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1); - -#if GTK_CHECK_VERSION(2,2,0) - gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, pixbuf, - 0, 0, 4, 4, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); -#else - gdk_pixbuf_render_to_drawable(pixbuf, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 4, 4, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); -#endif - - gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, TRUE, - NULL, gtkblist->tipwindow, "tooltip", 38, 4, layout); - - g_object_unref (pixbuf); - g_object_unref (layout); - g_free(tooltiptext); - return; -} - -static gboolean gaim_gtk_blist_tooltip_timeout(GtkWidget *tv) -{ - GtkTreePath *path; - GtkTreeIter iter; - GaimBlistNode *node; - GValue val = {0}; - - if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->rect.x, gtkblist->rect.y, &path, NULL, NULL, NULL)) - return FALSE; - gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); - gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); - node = g_value_get_pointer(&val); - - if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - int scr_w,scr_h, w, h, x, y; - PangoLayout *layout; - struct buddy *buddy = (struct buddy*)node; - char *tooltiptext = gaim_get_tooltip_text(buddy); - gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP); - gtkblist->tipwindow->parent = tv; - gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE); - gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE); - gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips"); - g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", - G_CALLBACK(gaim_gtk_blist_paint_tip), buddy); - gtk_widget_ensure_style (gtkblist->tipwindow); - - layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); - pango_layout_set_wrap(layout, PANGO_WRAP_WORD); - pango_layout_set_width(layout, 300000); - pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); - scr_w = gdk_screen_width(); - scr_h = gdk_screen_height(); - pango_layout_get_size (layout, &w, &h); - w = PANGO_PIXELS(w) + 8; - h = PANGO_PIXELS(h) + 8; - - /* 38 is the size of a large status icon plus 4 pixels padding on each side. - I should #define this or something */ - w = w + 38; - h = MAX(h, 38); - - gdk_window_get_pointer(NULL, &x, &y, NULL); - if (GTK_WIDGET_NO_WINDOW(gtkblist->window)) - y+=gtkblist->window->allocation.y; - - x -= ((w >> 1) + 4); - - if ((x + w) > scr_w) - x -= (x + w) - scr_w; - else if (x < 0) - x = 0; - - if ((y + h + 4) > scr_h) - y = y - h; - else - y = y + 6; - g_object_unref (layout); - g_free(tooltiptext); - gtk_widget_set_size_request(gtkblist->tipwindow, w, h); - gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y); - gtk_widget_show(gtkblist->tipwindow); - } - - gtk_tree_path_free(path); - return FALSE; -} - -static gboolean gaim_gtk_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) -{ - GtkTreePath *path; - if (gtkblist->timeout) { - if ((event->y > gtkblist->rect.y) && ((event->y - gtkblist->rect.height) < gtkblist->rect.y)) - return FALSE; - /* We've left the cell. Remove the timeout and create a new one below */ - if (gtkblist->tipwindow) { - gtk_widget_destroy(gtkblist->tipwindow); - gtkblist->tipwindow = NULL; - } - - g_source_remove(gtkblist->timeout); - } - - gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); - gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->rect); - if (path) - gtk_tree_path_free(path); - gtkblist->timeout = g_timeout_add(500, (GSourceFunc)gaim_gtk_blist_tooltip_timeout, tv); - return FALSE; -} - -static void gaim_gtk_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) -{ - if (gtkblist->timeout) { - g_source_remove(gtkblist->timeout); - gtkblist->timeout = 0; - } - if (gtkblist->tipwindow) { - gtk_widget_destroy(gtkblist->tipwindow); - gtkblist->tipwindow = NULL; - } -} - -static void -toggle_debug(void) -{ - misc_options ^= OPT_MISC_DEBUG; - - if ((misc_options & OPT_MISC_DEBUG)) - gaim_gtk_debug_window_show(); - else - gaim_gtk_debug_window_hide(); - - save_prefs(); -} - - -/*************************************************** - * Crap * - ***************************************************/ -static GtkItemFactoryEntry blist_menu[] = -{ - /* Buddies menu */ - { N_("/_Buddies"), NULL, NULL, 0, "" }, - { N_("/Buddies/New _Instant Message..."), "I", show_im_dialog, 0, "", GAIM_STOCK_IM }, - { N_("/Buddies/Join a _Chat..."), "C", join_chat, 0, "", GAIM_STOCK_CHAT }, - { N_("/Buddies/Get _User Info..."), "J", show_info_dialog, 0, "", GAIM_STOCK_INFO }, - { "/Buddies/sep1", NULL, NULL, 0, "" }, - { N_("/Buddies/_Show Offline Buddies"), NULL, gaim_gtk_blist_edit_mode_cb, 1, ""}, - { N_("/Buddies/Show _Empty Groups"), NULL, gaim_gtk_blist_show_empty_groups_cb, 1, ""}, - { N_("/Buddies/_Add a Buddy..."), NULL, gaim_gtk_blist_add_buddy_cb, 0, "", GTK_STOCK_ADD }, - { N_("/Buddies/Add a _Group..."), NULL, show_add_group, 0, NULL}, - { "/Buddies/sep2", NULL, NULL, 0, "" }, - { N_("/Buddies/_Signoff"), "D", signoff_all, 0, "", GAIM_STOCK_SIGN_OFF }, - { N_("/Buddies/_Quit"), "Q", do_quit, 0, "", GTK_STOCK_QUIT }, - - /* Tools */ - { N_("/_Tools"), NULL, NULL, 0, "" }, - { N_("/Tools/_Away"), NULL, NULL, 0, "" }, - { N_("/Tools/Buddy _Pounce"), NULL, NULL, 0, "" }, - { N_("/Tools/P_rotocol Actions"), NULL, NULL, 0, "" }, - { "/Tools/sep1", NULL, NULL, 0, "" }, - { N_("/Tools/A_ccounts..."), "A", account_editor, 0, "", GAIM_STOCK_ACCOUNTS }, - { N_("/Tools/_File Transfers..."), NULL, gaim_show_xfer_dialog, 0, "", GAIM_STOCK_FILE_TRANSFER }, - { N_("/Tools/Preferences..."), "P", show_prefs, 0, "", GTK_STOCK_PREFERENCES }, - { N_("/Tools/Pr_ivacy..."), NULL, show_privacy_options, 0, "", GAIM_STOCK_PRIVACY }, - { "/Tools/sep2", NULL, NULL, 0, "" }, - { N_("/Tools/View System _Log..."), NULL, gtk_blist_show_systemlog_cb, 0, NULL }, - - /* Help */ - { N_("/_Help"), NULL, NULL, 0, "" }, - { N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "", GTK_STOCK_HELP }, - { N_("/Help/_Debug Window..."), NULL, toggle_debug, 0, NULL }, - { N_("/Help/_About..."), NULL, show_about, 0, "", GAIM_STOCK_ABOUT }, -}; - -/********************************************************* - * Private Utility functions * - *********************************************************/ - -static char *gaim_get_tooltip_text(struct buddy *b) -{ - GaimPlugin *prpl; - GaimPluginProtocolInfo *prpl_info = NULL; - char *text = NULL; - char *statustext = NULL; - char *aliastext = NULL, *nicktext = NULL; - char *warning = NULL, *idletime = NULL; - - prpl = gaim_find_prpl(b->account->protocol); - prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->tooltip_text) { - const char *end; - statustext = prpl_info->tooltip_text(b); - - if(statustext && !g_utf8_validate(statustext, -1, &end)) { - char *new = g_strndup(statustext, - g_utf8_pointer_to_offset(statustext, end)); - g_free(statustext); - statustext = new; - } - } - - if (!statustext && !GAIM_BUDDY_IS_ONLINE(b)) - statustext = g_strdup(_("Status: Offline")); - - if (b->idle > 0) { - int ihrs, imin; - time_t t; - time(&t); - ihrs = (t - b->idle) / 3600; - imin = ((t - b->idle) / 60) % 60; - if (ihrs) - idletime = g_strdup_printf(_("%dh%02dm"), ihrs, imin); - else - idletime = g_strdup_printf(_("%dm"), imin); - } - - if(b->alias && b->alias[0]) - aliastext = g_markup_escape_text(b->alias, -1); - - if(b->server_alias) - nicktext = g_markup_escape_text(b->server_alias, -1); - - if (b->evil > 0) - warning = g_strdup_printf(_("%d%%"), b->evil); - - text = g_strdup_printf("%s" - "%s %s" /* Alias */ - "%s %s" /* Nickname */ - "%s %s" /* Idle */ - "%s %s" /* Warning */ - "%s%s" /* Status */ - "%s", - b->name, - aliastext ? _("\nAlias:") : "", aliastext ? aliastext : "", - nicktext ? _("\nNickname:") : "", nicktext ? nicktext : "", - idletime ? _("\nIdle:") : "", idletime ? idletime : "", - b->evil ? _("\nWarned:") : "", b->evil ? warning : "", - statustext ? "\n" : "", statustext ? statustext : "", - !g_ascii_strcasecmp(b->name, "robflynn") ? _("\nDescription: Spooky") : ""); - - if(warning) - g_free(warning); - if(idletime) - g_free(idletime); - if(statustext) - g_free(statustext); - if(nicktext) - g_free(nicktext); - if(aliastext) - g_free(aliastext); - - return text; - -} - -GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b, GaimStatusIconSize size) -{ - GdkPixbuf *status = NULL; - GdkPixbuf *scale = NULL; - GdkPixbuf *emblem = NULL; - gchar *filename = NULL; - const char *protoname = NULL; - - char *se = NULL, *sw = NULL ,*nw = NULL ,*ne = NULL; - - int scalesize = 30; - - GaimPlugin *prpl; - GaimPluginProtocolInfo *prpl_info = NULL; - - prpl = gaim_find_prpl(b->account->protocol); - - if (!prpl) - return NULL; - - prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->list_icon) - protoname = prpl_info->list_icon(b->account, b); - if (b->present != GAIM_BUDDY_SIGNING_OFF && prpl_info->list_emblems) - prpl_info->list_emblems(b, &se, &sw, &nw, &ne); - - if (size == GAIM_STATUS_ICON_SMALL) { - scalesize = 15; - sw = nw = ne = NULL; /* So that only the se icon will composite */ - } - - - if (b->present == GAIM_BUDDY_SIGNING_ON) { - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL); - status = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - } else if (b->present == GAIM_BUDDY_SIGNING_OFF) { - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "logout.png", NULL); - status = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - - /* "Hey, what's all this crap?" you ask. Status icons will be themeable too, and - then it will look up protoname from the theme */ - } else { - char *image = g_strdup_printf("%s.png", protoname); - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); - status = gdk_pixbuf_new_from_file(filename,NULL); - g_free(image); - g_free(filename); - - } - - if (!status) - return NULL; - - scale = gdk_pixbuf_scale_simple(status, scalesize, scalesize, GDK_INTERP_BILINEAR); - - g_object_unref(G_OBJECT(status)); - - /* Emblems */ - - /* Each protocol can specify up to four "emblems" to composite over the base icon. "away", "busy", "mobile user" - * are all examples of states represented by emblems. I'm not even really sure I like this yet. */ - - /* XXX Clean this crap up, yo. */ - if (se) { - char *image = g_strdup_printf("%s.png", se); - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); - g_free(image); - emblem = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - if (emblem) { - if (size == GAIM_STATUS_ICON_LARGE) - gdk_pixbuf_composite (emblem, - scale, 15, 15, - 15, 15, - 15, 15, - 1, 1, - GDK_INTERP_BILINEAR, - 255); - else - gdk_pixbuf_composite (emblem, - scale, 5, 5, - 10, 10, - 5, 5, - .6, .6, - GDK_INTERP_BILINEAR, - 255); - g_object_unref(G_OBJECT(emblem)); - } - } - if (sw) { - char *image = g_strdup_printf("%s.png", sw); - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); - g_free(image); - emblem = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - if (emblem) { - gdk_pixbuf_composite (emblem, - scale, 0, 15, - 15, 15, - 0, 15, - 1, 1, - GDK_INTERP_BILINEAR, - 255); - g_object_unref(G_OBJECT(emblem)); - } - } - if (nw) { - char *image = g_strdup_printf("%s.png", nw); - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); - g_free(image); - emblem = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - if (emblem) { - gdk_pixbuf_composite (emblem, - scale, 0, 0, - 15, 15, - 0, 0, - 1, 1, - GDK_INTERP_BILINEAR, - 255); - g_object_unref(G_OBJECT(emblem)); - } - } - if (ne) { - char *image = g_strdup_printf("%s.png", ne); - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); - g_free(image); - emblem = gdk_pixbuf_new_from_file(filename,NULL); - g_free(filename); - if (emblem) { - gdk_pixbuf_composite (emblem, - scale, 15, 0, - 15, 15, - 15, 0, - 1, 1, - GDK_INTERP_BILINEAR, - 255); - g_object_unref(G_OBJECT(emblem)); - } - } - - - /* Idle grey buddies affects the whole row. This converts the status icon to greyscale. */ - if (b->present == GAIM_BUDDY_OFFLINE) - gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); - else if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS) - gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.25, FALSE); - return scale; -} - -static GdkPixbuf *gaim_gtk_blist_get_buddy_icon(struct buddy *b) -{ - /* This just opens a file from ~/.gaim/icons/screenname. This needs to change to be more gooder. */ - char *file; - GdkPixbuf *buf, *ret; - - if (!(blist_options & OPT_BLIST_SHOW_ICONS)) - return NULL; - - if ((file = gaim_buddy_get_setting(b, "buddy_icon")) == NULL) - return NULL; - - buf = gdk_pixbuf_new_from_file(file, NULL); - g_free(file); - - - if (buf) { - if (!GAIM_BUDDY_IS_ONLINE(b)) - gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE); - if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS) - gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE); - - ret = gdk_pixbuf_scale_simple(buf,30,30, GDK_INTERP_BILINEAR); - g_object_unref(G_OBJECT(buf)); - return ret; - } - return NULL; -} - -static gchar *gaim_gtk_blist_get_name_markup(struct buddy *b, gboolean selected) -{ - char *name = gaim_get_buddy_alias(b); - char *esc = g_markup_escape_text(name, strlen(name)), *text = NULL; - GaimPlugin *prpl; - GaimPluginProtocolInfo *prpl_info = NULL; - /* XXX Clean up this crap */ - - int ihrs, imin; - char *idletime = NULL, *warning = NULL, *statustext = NULL; - time_t t; - - prpl = gaim_find_prpl(b->account->protocol); - - if (prpl != NULL) - prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); - - if (!(blist_options & OPT_BLIST_SHOW_ICONS)) { - if ((b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) || !GAIM_BUDDY_IS_ONLINE(b)) { - text = g_strdup_printf("%s", - esc); - g_free(esc); - return text; - } else { - return esc; - } - } - - time(&t); - ihrs = (t - b->idle) / 3600; - imin = ((t - b->idle) / 60) % 60; - - if (prpl && prpl_info->status_text) { - char *tmp = prpl_info->status_text(b); - const char *end; - - if(tmp && !g_utf8_validate(tmp, -1, &end)) { - char *new = g_strndup(tmp, - g_utf8_pointer_to_offset(tmp, end)); - g_free(tmp); - tmp = new; - } - - if(tmp) { - char buf[32]; - char *c = tmp; - int length = 0, vis=0; - gboolean inside = FALSE; - g_strdelimit(tmp, "\n", ' '); - - while(*c && vis < 20) { - if(*c == '&') - inside = TRUE; - else if(*c == ';') - inside = FALSE; - if(!inside) - vis++; - length++; - c++; /* this is fun */ - } - - if(vis == 20) - g_snprintf(buf, sizeof(buf), "%%.%ds...", length); - else - g_snprintf(buf, sizeof(buf), "%%s "); - - statustext = g_strdup_printf(buf, tmp); - - g_free(tmp); - } - } - - if (b->idle > 0) { - if (ihrs) - idletime = g_strdup_printf(_("Idle (%dh%02dm) "), ihrs, imin); - else - idletime = g_strdup_printf(_("Idle (%dm) "), imin); - } - - if (b->evil > 0) - warning = g_strdup_printf(_("Warned (%d%%) "), b->evil); - - if(!GAIM_BUDDY_IS_ONLINE(b) && !statustext) - statustext = g_strdup("Offline "); - - if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) { - text = g_strdup_printf("%s\n" - "%s%s%s", - esc, - statustext != NULL ? statustext : "", - idletime != NULL ? idletime : "", - warning != NULL ? warning : ""); - } else if (statustext == NULL && idletime == NULL && warning == NULL && GAIM_BUDDY_IS_ONLINE(b)) { - text = g_strdup(esc); - } else { - text = g_strdup_printf("%s\n" - "%s%s%s", esc, - selected ? "" : "color='dim grey'", - statustext != NULL ? statustext : "", - idletime != NULL ? idletime : "", - warning != NULL ? warning : ""); - } - if (idletime) - g_free(idletime); - if (warning) - g_free(warning); - if (statustext) - g_free(statustext); - if (esc) - g_free(esc); - - return text; -} - -static void gaim_gtk_blist_restore_position() -{ - /* if the window exists, is hidden, we're saving positions, and the position is sane... */ - if(gtkblist && gtkblist->window && - !GTK_WIDGET_VISIBLE(gtkblist->window) && - blist_pos.width != 0) { - /* ...check position is on screen... */ - if (blist_pos.x >= gdk_screen_width()) - blist_pos.x = gdk_screen_width() - 100; - else if (blist_pos.x < 0) - blist_pos.x = 100; - - if (blist_pos.y >= gdk_screen_height()) - blist_pos.y = gdk_screen_height() - 100; - else if (blist_pos.y < 0) - blist_pos.y = 100; - - /* ...and move it back. */ - gtk_window_move(GTK_WINDOW(gtkblist->window), blist_pos.x, blist_pos.y); - gtk_window_resize(GTK_WINDOW(gtkblist->window), blist_pos.width, blist_pos.height); - } -} - -static gboolean gaim_gtk_blist_refresh_timer(struct gaim_buddy_list *list) -{ - GaimBlistNode *group = list->root; - GaimBlistNode *buddy; - - while (group) { - buddy = group->child; - while (buddy) { - if (((struct buddy *)buddy)->idle) - gaim_gtk_blist_update(list, buddy); - buddy = buddy->next; - } - group = group->next; - } - - /* keep on going */ - return TRUE; -} - -/********************************************************************************** - * Public API Functions * - **********************************************************************************/ -static void gaim_gtk_blist_new_list(struct gaim_buddy_list *blist) -{ - blist->ui_data = g_new0(struct gaim_gtk_buddy_list, 1); -} - -void gaim_gtk_blist_update_columns() -{ - if(!gtkblist) - return; - - if (blist_options & OPT_BLIST_SHOW_ICONS) { - gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, TRUE); - gtk_tree_view_column_set_visible(gtkblist->idle_column, FALSE); - gtk_tree_view_column_set_visible(gtkblist->warning_column, FALSE); - } else { - gtk_tree_view_column_set_visible(gtkblist->idle_column, blist_options & OPT_BLIST_SHOW_IDLETIME); - gtk_tree_view_column_set_visible(gtkblist->warning_column, blist_options & OPT_BLIST_SHOW_WARN); - gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, FALSE); - } -} - -enum {DRAG_BUDDY, DRAG_ROW}; - -static char * -item_factory_translate_func (const char *path, gpointer func_data) -{ - return _(path); -} - -static void gaim_gtk_blist_show(struct gaim_buddy_list *list) -{ - GtkItemFactory *ift; - GtkCellRenderer *rend; - GtkTreeViewColumn *column; - GtkWidget *sw; - GtkWidget *button; - GtkSizeGroup *sg; - GtkAccelGroup *accel_group; - GtkTreeSelection *selection; - GtkTargetEntry gte[] = {{"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW}, - {"application/x-im-contact", 0, DRAG_BUDDY}}; - - if (gtkblist && gtkblist->window) { - gtk_widget_show(gtkblist->window); - return; - } - - gtkblist = GAIM_GTK_BLIST(list); - - gtkblist->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_role(GTK_WINDOW(gtkblist->window), "buddy_list"); - gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List")); - - GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE; - - gtkblist->vbox = gtk_vbox_new(FALSE, 0); - gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->vbox); - - g_signal_connect(G_OBJECT(gtkblist->window), "delete_event", G_CALLBACK(gtk_blist_delete_cb), NULL); - g_signal_connect(G_OBJECT(gtkblist->window), "configure_event", G_CALLBACK(gtk_blist_configure_cb), NULL); - g_signal_connect(G_OBJECT(gtkblist->window), "visibility_notify_event", G_CALLBACK(gtk_blist_visibility_cb), NULL); - gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK); - - /******************************* Menu bar *************************************/ - accel_group = gtk_accel_group_new(); - gtk_window_add_accel_group(GTK_WINDOW (gtkblist->window), accel_group); - g_object_unref(accel_group); - ift = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "", accel_group); - gtk_item_factory_set_translate_func (ift, - item_factory_translate_func, - NULL, NULL); - gtk_item_factory_create_items(ift, sizeof(blist_menu) / sizeof(*blist_menu), - blist_menu, NULL); - gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtk_item_factory_get_widget(ift, ""), FALSE, FALSE, 0); - - awaymenu = gtk_item_factory_get_widget(ift, N_("/Tools/Away")); - do_away_menu(); - - gtkblist->bpmenu = gtk_item_factory_get_widget(ift, N_("/Tools/Buddy Pounce")); - gaim_gtkpounce_menu_build(gtkblist->bpmenu); - - protomenu = gtk_item_factory_get_widget(ift, N_("/Tools/Protocol Actions")); - do_proto_menu(); - - /****************************** GtkTreeView **********************************/ - sw = gtk_scrolled_window_new(NULL,NULL); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - - gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER); - - gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel)); - gtk_widget_set_size_request(gtkblist->treeview, -1, 200); - - /* Set up selection stuff */ - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); - g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(gaim_gtk_blist_selection_changed), NULL); - - - /* Set up dnd */ - gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(gtkblist->treeview), GDK_BUTTON1_MASK, gte, - 2, GDK_ACTION_COPY); - gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), gte, 2, - GDK_ACTION_COPY | GDK_ACTION_MOVE); - g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(gaim_gtk_blist_drag_data_rcv_cb), NULL); - g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(gaim_gtk_blist_drag_data_get_cb), NULL); - - /* Tooltips */ - g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(gaim_gtk_blist_motion_cb), NULL); - g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(gaim_gtk_blist_leave_cb), NULL); - - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); - - column = gtk_tree_view_column_new (); - - rend = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start (column, rend, FALSE); - gtk_tree_view_column_set_attributes (column, rend, - "pixbuf", STATUS_ICON_COLUMN, - "visible", STATUS_ICON_VISIBLE_COLUMN, - NULL); - g_object_set(rend, "xalign", 0.0, "ypad", 0, NULL); - - rend = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start (column, rend, TRUE); - gtk_tree_view_column_set_attributes (column, rend, - "markup", NAME_COLUMN, - NULL); - g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); - - gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); - - rend = gtk_cell_renderer_text_new(); - gtkblist->warning_column = gtk_tree_view_column_new_with_attributes("Warning", rend, "markup", WARNING_COLUMN, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->warning_column); - g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); - - rend = gtk_cell_renderer_text_new(); - gtkblist->idle_column = gtk_tree_view_column_new_with_attributes("Idle", rend, "markup", IDLE_COLUMN, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->idle_column); - g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); - - rend = gtk_cell_renderer_pixbuf_new(); - gtkblist->buddy_icon_column = gtk_tree_view_column_new_with_attributes("Buddy Icon", rend, "pixbuf", BUDDY_ICON_COLUMN, NULL); - g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); - gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->buddy_icon_column); - - g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL); - g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL); - g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", G_CALLBACK(gtk_blist_row_collapsed_cb), NULL); - g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL); - - gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sw, TRUE, TRUE, 0); - gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview); - gaim_gtk_blist_update_columns(); - - /* set the Show Offline Buddies option. must be done - * after the treeview or faceprint gets mad. -Robot101 - */ - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (ift, N_("/Buddies/Show Offline Buddies"))), - blist_options & OPT_BLIST_SHOW_OFFLINE); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (ift, N_("/Buddies/Show Empty Groups"))), - !(blist_options & OPT_BLIST_NO_MT_GRP)); - - /* OK... let's show this bad boy. */ - gaim_gtk_blist_refresh(list); - gaim_gtk_blist_restore_position(); - gtk_widget_show_all(gtkblist->window); - - /**************************** Button Box **************************************/ - /* add this afterwards so it doesn't force up the width of the window */ - - gtkblist->tooltips = gtk_tooltips_new(); - - sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); - gtkblist->bbox = gtk_hbox_new(TRUE, 0); - gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->bbox, FALSE, FALSE, 0); - gtk_widget_show(gtkblist->bbox); - - button = gaim_pixbuf_button_from_stock(_("IM"), GAIM_STOCK_IM, GAIM_BUTTON_VERTICAL); - gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_size_group_add_widget(sg, button); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_im_cb), - gtkblist->treeview); - gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Send a message to the selected buddy"), NULL); - gtk_widget_show(button); - - button = gaim_pixbuf_button_from_stock(_("Get Info"), GAIM_STOCK_INFO, GAIM_BUTTON_VERTICAL); - gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_size_group_add_widget(sg, button); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_info_cb), - gtkblist->treeview); - gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Get information on the selected buddy"), NULL); - gtk_widget_show(button); - - button = gaim_pixbuf_button_from_stock(_("Chat"), GAIM_STOCK_CHAT, GAIM_BUTTON_VERTICAL); - gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_size_group_add_widget(sg, button); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_chat_cb), NULL); - gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Join a chat room"), NULL); - gtk_widget_show(button); - - button = gaim_pixbuf_button_from_stock(_("Away"), GAIM_STOCK_ICON_AWAY, GAIM_BUTTON_VERTICAL); - gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); - gtk_size_group_add_widget(sg, button); - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_away_cb), NULL); - gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Set an away message"), NULL); - gtk_widget_show(button); - - /* this will show the right image/label widgets for us */ - gaim_gtk_blist_update_toolbar(); - - /* start the refresh timer */ - gtkblist->refresh_timer = g_timeout_add(30000, (GSourceFunc)gaim_gtk_blist_refresh_timer, list); -} - -void gaim_gtk_blist_refresh(struct gaim_buddy_list *list) -{ - GaimBlistNode *group = list->root; - GaimBlistNode *buddy; - - while (group) { - buddy = group->child; - gaim_gtk_blist_update(list, group); - while (buddy) { - gaim_gtk_blist_update(list, buddy); - buddy = buddy->next; - } - group = group->next; - } -} - -static gboolean get_iter_from_node_helper(GaimBlistNode *node, GtkTreeIter *iter, GtkTreeIter *root) { - - do { - GaimBlistNode *n; - GtkTreeIter child; - - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), root, NODE_COLUMN, &n, -1); - if(n == node) { - *iter = *root; - return TRUE; - } - - if(gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &child, root)) { - if(get_iter_from_node_helper(node,iter,&child)) - return TRUE; - } - } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(gtkblist->treemodel), root)); - - return FALSE; -} - -static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter) { - GtkTreeIter root; - - if (!gtkblist) - return FALSE; - - if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(gtkblist->treemodel), &root)) - return FALSE; - - return get_iter_from_node_helper(node, iter, &root); -} - -/* - * These state assignments suck. I'm sorry. They're for historical reasons. - * Roll on new prefs. -Robot101 - * - * NO_BUTTON_TEXT && SHOW_BUTTON_XPM - image - * !NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - text - * !NO_BUTTON_TEXT && SHOW_BUTTON_XPM - text & images - * NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - none - */ - -static void gaim_gtk_blist_update_toolbar_icons (GtkWidget *widget, gpointer data) { - if (GTK_IS_IMAGE(widget)) { - if (blist_options & OPT_BLIST_SHOW_BUTTON_XPM) - gtk_widget_show(widget); - else - gtk_widget_hide(widget); - } else if (GTK_IS_LABEL(widget)) { - if (blist_options & OPT_BLIST_NO_BUTTON_TEXT) - gtk_widget_hide(widget); - else - gtk_widget_show(widget); - } else if (GTK_IS_CONTAINER(widget)) { - gtk_container_foreach(GTK_CONTAINER(widget), gaim_gtk_blist_update_toolbar_icons, NULL); - } -} - -void gaim_gtk_blist_update_toolbar() { - if (!gtkblist) - return; - - if (blist_options & OPT_BLIST_NO_BUTTON_TEXT && !(blist_options & OPT_BLIST_SHOW_BUTTON_XPM)) - gtk_widget_hide(gtkblist->bbox); - else { - gtk_container_foreach(GTK_CONTAINER(gtkblist->bbox), gaim_gtk_blist_update_toolbar_icons, NULL); - gtk_widget_show(gtkblist->bbox); - } -} - -static void gaim_gtk_blist_remove(struct gaim_buddy_list *list, GaimBlistNode *node) -{ - GtkTreeIter iter; - - /* For some reason, we're called before we have a buddy list sometimes */ - if(!gtkblist) - return; - - if(gtkblist->selected_node == node) - gtkblist->selected_node = NULL; - - if (get_iter_from_node(node, &iter)) { - gtk_tree_store_remove(gtkblist->treemodel, &iter); - if(GAIM_BLIST_NODE_IS_BUDDY(node)) { - gaim_gtk_blist_update(list, node->parent); - } - } -} - -static gboolean do_selection_changed(GaimBlistNode *new_selection) -{ - GaimBlistNode *old_selection = gtkblist->selected_node; - - if(new_selection != gtkblist->selected_node) { - gtkblist->selected_node = new_selection; - if(new_selection) - gaim_gtk_blist_update(NULL, new_selection); - if(old_selection) - gaim_gtk_blist_update(NULL, old_selection); - } - - return FALSE; -} - -static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data) -{ - GaimBlistNode *new_selection = NULL; - GtkTreeIter iter; - - if(gtk_tree_selection_get_selected(selection, NULL, &iter)){ - gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, - NODE_COLUMN, &new_selection, -1); - } - /* we set this up as a timeout, otherwise the blist flickers */ - g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection); -} - -static void make_a_group(GaimBlistNode *node, GtkTreeIter *iter) { - GaimBlistNode *sibling; - GtkTreeIter siblingiter; - struct group *group = (struct group *)node; - char *esc = g_markup_escape_text(group->name, -1); - char *mark; - - if(blist_options & OPT_BLIST_SHOW_GRPNUM) - mark = g_strdup_printf("%s (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE)); - else - mark = g_strdup_printf("%s", esc); - - g_free(esc); - - sibling = node->prev; - while (sibling && !get_iter_from_node(sibling, &siblingiter)) { - sibling = sibling->prev; - } - - gtk_tree_store_insert_after(gtkblist->treemodel, iter, NULL, - sibling ? &siblingiter : NULL); - gtk_tree_store_set(gtkblist->treemodel, iter, - STATUS_ICON_COLUMN, NULL, - STATUS_ICON_VISIBLE_COLUMN, FALSE, - NAME_COLUMN, mark, - NODE_COLUMN, node, - -1); - g_free(mark); -} - - -static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node) -{ - GtkTreeIter iter; - GtkTreePath *expand = NULL; - gboolean new_entry = FALSE; - - if (!gtkblist) - return; - - if (!get_iter_from_node(node, &iter)) { /* This is a newly added node */ - new_entry = TRUE; - if (GAIM_BLIST_NODE_IS_BUDDY(node)) { - if (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc)) { - GtkTreeIter groupiter; - GaimBlistNode *oldersibling; - GtkTreeIter oldersiblingiter; - char *collapsed = gaim_group_get_setting((struct group *)node->parent, "collapsed"); - - if(node->parent && - !get_iter_from_node(node->parent, &groupiter)) { - /* This buddy's group has not yet been added. - * We do that here */ - make_a_group(node->parent, &groupiter); - } - if(!collapsed) - expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter); - else - g_free(collapsed); - - oldersibling = node->prev; - while (oldersibling && !get_iter_from_node(oldersibling, &oldersiblingiter)) { - oldersibling = oldersibling->prev; - } - - gtk_tree_store_insert_after(gtkblist->treemodel, &iter, &groupiter, oldersibling ? &oldersiblingiter : NULL); - - if (blist_options & OPT_BLIST_POPUP) { - gtk_widget_show(gtkblist->window); - gtk_window_deiconify(GTK_WINDOW(gtkblist->window)); - gdk_window_raise(gtkblist->window->window); - } - - } - } - else if (GAIM_BLIST_NODE_IS_GROUP(node) && - ((blist_options & OPT_BLIST_SHOW_OFFLINE) || - !(blist_options & OPT_BLIST_NO_MT_GRP))) { - make_a_group(node, &iter); - expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); - } - } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { - if((blist_options & OPT_BLIST_NO_MT_GRP) && !(blist_options & OPT_BLIST_SHOW_OFFLINE) && !gaim_blist_get_group_online_count((struct group *)node)) { - gtk_tree_store_remove(gtkblist->treemodel, &iter); - } else { - struct group *group = (struct group *)node; - char *esc = g_markup_escape_text(group->name, -1); - char *mark; - - if(blist_options & OPT_BLIST_SHOW_GRPNUM) - mark = g_strdup_printf("%s (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE)); - else - mark = g_strdup_printf("%s", esc); - - g_free(esc); - gtk_tree_store_set(gtkblist->treemodel, &iter, - NAME_COLUMN, mark, - -1); - g_free(mark); - } - } - - if (GAIM_BLIST_NODE_IS_BUDDY(node) && (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc))) { - GdkPixbuf *status, *avatar; - char *mark; - char *warning = NULL, *idle = NULL; - - gboolean selected = (gtkblist->selected_node == node); - - status = gaim_gtk_blist_get_status_icon((struct buddy*)node, - blist_options & OPT_BLIST_SHOW_ICONS ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL); - avatar = gaim_gtk_blist_get_buddy_icon((struct buddy*)node); - mark = gaim_gtk_blist_get_name_markup((struct buddy*)node, selected); - - if (((struct buddy*)node)->idle > 0) { - time_t t; - int ihrs, imin; - time(&t); - ihrs = (t - ((struct buddy *)node)->idle) / 3600; - imin = ((t - ((struct buddy*)node)->idle) / 60) % 60; - if(ihrs > 0) - idle = g_strdup_printf("(%d:%02d)", ihrs, imin); - else - idle = g_strdup_printf("(%d)", imin); - } - - if (((struct buddy*)node)->evil > 0) - warning = g_strdup_printf("%d%%", ((struct buddy*)node)->evil); - - - if((blist_options & OPT_BLIST_GREY_IDLERS) - && ((struct buddy *)node)->idle) { - if(warning && !selected) { - char *w2 = g_strdup_printf("%s", - warning); - g_free(warning); - warning = w2; - } - - if(idle && !selected) { - char *i2 = g_strdup_printf("%s", - idle); - g_free(idle); - idle = i2; - } - } - - gtk_tree_store_set(gtkblist->treemodel, &iter, - STATUS_ICON_COLUMN, status, - STATUS_ICON_VISIBLE_COLUMN, TRUE, - NAME_COLUMN, mark, - WARNING_COLUMN, warning, - IDLE_COLUMN, idle, - BUDDY_ICON_COLUMN, avatar, - NODE_COLUMN, node, - -1); - - if (blist_options & OPT_BLIST_POPUP && - ((struct buddy *)node)->present == GAIM_BUDDY_SIGNING_OFF) { - gtk_widget_show(gtkblist->window); - gtk_window_deiconify(GTK_WINDOW(gtkblist->window)); - gdk_window_raise(gtkblist->window->window); - } - - g_free(mark); - if (idle) - g_free(idle); - if (warning) - g_free(warning); - - if (status != NULL) - g_object_unref(status); - - if (avatar != NULL) - g_object_unref(avatar); - - } else if (GAIM_BLIST_NODE_IS_BUDDY(node) && !new_entry) { - gaim_gtk_blist_remove(list, node); - } - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview)); - - - if(expand) { - gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, TRUE); - gtk_tree_path_free(expand); - } - - if(GAIM_BLIST_NODE_IS_BUDDY(node)) - gaim_gtk_blist_update(list, node->parent); -} - -static void gaim_gtk_blist_destroy(struct gaim_buddy_list *list) -{ - if (!gtkblist) - return; - - gtk_widget_destroy(gtkblist->window); - gtk_object_sink(GTK_OBJECT(gtkblist->tooltips)); - - if (gtkblist->refresh_timer) - g_source_remove(gtkblist->refresh_timer); - if (gtkblist->timeout) - g_source_remove(gtkblist->timeout); - - gtkblist->refresh_timer = 0; - gtkblist->timeout = 0; - gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL; - gtkblist->treemodel = NULL; - gtkblist->idle_column = NULL; - gtkblist->warning_column = gtkblist->buddy_icon_column = NULL; - gtkblist->bbox = gtkblist->tipwindow = NULL; - protomenu = NULL; - awaymenu = NULL; - gtkblist = NULL; -} - -static void gaim_gtk_blist_set_visible(struct gaim_buddy_list *list, gboolean show) -{ - if (!(gtkblist && gtkblist->window)) - return; - - if (show) { - gaim_gtk_blist_restore_position(); - gtk_window_present(GTK_WINDOW(gtkblist->window)); - } else { - if (!connections || docklet_count) { -#ifdef _WIN32 - wgaim_systray_minimize(gtkblist->window); -#endif - gtk_widget_hide(gtkblist->window); - } else { - gtk_window_iconify(GTK_WINDOW(gtkblist->window)); - } - } -} - -void gaim_gtk_blist_docklet_toggle() { - /* Useful for the docklet plugin and also for the win32 tray icon*/ - /* This is called when one of those is clicked--it will show/hide the - buddy list/login window--depending on which is active */ - if (connections) { - if (gtkblist && gtkblist->window) { - if (GTK_WIDGET_VISIBLE(gtkblist->window)) { - gaim_blist_set_visible(GAIM_WINDOW_ICONIFIED(gtkblist->window) || gaim_gtk_blist_obscured); - } else { -#if _WIN32 - wgaim_systray_maximize(gtkblist->window); -#endif - gaim_blist_set_visible(TRUE); - } - } else { - /* we're logging in or something... do nothing */ - /* or should I make the blist? */ - gaim_debug(GAIM_DEBUG_WARNING, "blist", - "docklet_toggle called with connections " - "but no blist!\n"); - } - } else if (mainwindow) { - if (GTK_WIDGET_VISIBLE(mainwindow)) { - if (GAIM_WINDOW_ICONIFIED(mainwindow)) { - gtk_window_present(GTK_WINDOW(mainwindow)); - } else { -#if _WIN32 - wgaim_systray_minimize(mainwindow); -#endif - gtk_widget_hide(mainwindow); - } - } else { -#if _WIN32 - wgaim_systray_maximize(mainwindow); -#endif - show_login(); - } - } else { - show_login(); - } -} - -void gaim_gtk_blist_docklet_add() -{ - docklet_count++; -} - -void gaim_gtk_blist_docklet_remove() -{ - docklet_count--; - if (!docklet_count) { - if (connections) - gaim_blist_set_visible(TRUE); - else if (mainwindow) - gtk_window_present(GTK_WINDOW(mainwindow)); - else - show_login(); - } -} - -static struct gaim_blist_ui_ops blist_ui_ops = -{ - gaim_gtk_blist_new_list, - NULL, - gaim_gtk_blist_show, - gaim_gtk_blist_update, - gaim_gtk_blist_remove, - gaim_gtk_blist_destroy, - gaim_gtk_blist_set_visible -}; - - -struct gaim_blist_ui_ops *gaim_get_gtk_blist_ui_ops() -{ - return &blist_ui_ops; -} - - - -/********************************************************************* - * Public utility functions * - *********************************************************************/ - -GdkPixbuf * -create_prpl_icon(struct gaim_account *account) -{ - GaimPlugin *prpl; - GaimPluginProtocolInfo *prpl_info = NULL; - GdkPixbuf *status = NULL; - char *filename = NULL; - const char *protoname = NULL; - char buf[256]; - - prpl = gaim_find_prpl(account->protocol); - - if (prpl != NULL) { - prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info->list_icon != NULL) - protoname = prpl_info->list_icon(account, NULL); - } - - if (protoname == NULL) - return NULL; - - /* - * Status icons will be themeable too, and then it will look up - * protoname from the theme - */ - g_snprintf(buf, sizeof(buf), "%s.png", protoname); - - filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", - "default", buf, NULL); - status = gdk_pixbuf_new_from_file(filename, NULL); - g_free(filename); - - return status; -} - diff -r 6d1707dc8c3d -r 1a53330dfd34 src/dialogs.c --- a/src/dialogs.c Sat Apr 26 17:36:52 2003 +0000 +++ b/src/dialogs.c Sat Apr 26 17:56:23 2003 +0000 @@ -48,7 +48,7 @@ #include "gaim.h" #include "gtkimhtml.h" #include "prpl.h" -#include "gtklist.h" +#include "gtkblist.h" #ifdef _WIN32 #include "win32dep.h" diff -r 6d1707dc8c3d -r 1a53330dfd34 src/gaim.h --- a/src/gaim.h Sat Apr 26 17:36:52 2003 +0000 +++ b/src/gaim.h Sat Apr 26 17:56:23 2003 +0000 @@ -27,7 +27,7 @@ #endif #include "core.h" -#include "list.h" +#include "blist.h" #include "ui.h" #include "util.h" diff -r 6d1707dc8c3d -r 1a53330dfd34 src/gtkblist.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gtkblist.c Sat Apr 26 17:56:23 2003 +0000 @@ -0,0 +1,1824 @@ +/* + * gaim + * + * Copyright (C) 1998-1999, Mark Spencer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#ifdef GAIM_PLUGINS +#ifndef _WIN32 +#include +#endif +#endif /* GAIM_PLUGINS */ +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include +#include +#include "prpl.h" +#include "sound.h" +#include "gaim.h" +#include "gtkblist.h" +#include "gtkpounce.h" +#include "gtkft.h" + +#ifdef _WIN32 +#include "win32dep.h" +#endif + +static struct gaim_gtk_buddy_list *gtkblist = NULL; + +/* part of the best damn Docklet code this side of Tahiti */ +static gboolean gaim_gtk_blist_obscured = FALSE; + +static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data); +static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node); +static char *gaim_get_tooltip_text(struct buddy *b); +static char *item_factory_translate_func (const char *path, gpointer func_data); + +/*************************************************** + * Callbacks * + ***************************************************/ + +static gboolean gtk_blist_delete_cb(GtkWidget *w, GdkEventAny *event, gpointer data) +{ + if (docklet_count) + gaim_blist_set_visible(FALSE); + else + do_quit(); + + /* we handle everything, event should not propogate further */ + return TRUE; +} + +static gboolean gtk_blist_save_prefs_cb(gpointer data) +{ + save_prefs(); + + /* only run once */ + return FALSE; +} + +static gboolean gtk_blist_configure_cb(GtkWidget *w, GdkEventConfigure *event, gpointer data) +{ + /* unfortunately GdkEventConfigure ignores the window gravity, but * + * the only way we have of setting the position doesn't. we have to * + * call get_position and get_size because they do pay attention to * + * the gravity. this is inefficient and I agree it sucks, but it's * + * more likely to work correctly. - Robot101 */ + gint x, y; + + /* check for visibility because when we aren't visible, this will * + * give us bogus (0,0) coordinates. - xOr */ + if (GTK_WIDGET_VISIBLE(w)) { + gtk_window_get_position(GTK_WINDOW(w), &x, &y); + + if (x != blist_pos.x || + y != blist_pos.y || + event->width != blist_pos.width || + event->height != blist_pos.height) { + blist_pos.x = x; + blist_pos.y = y; + blist_pos.width = event->width; + blist_pos.height = event->height; + + if (!g_main_context_find_source_by_user_data(NULL, >k_blist_save_prefs_cb)) { + g_timeout_add(5000, gtk_blist_save_prefs_cb, >k_blist_save_prefs_cb); + } + } + } + + /* continue to handle event normally */ + return FALSE; +} + +static gboolean gtk_blist_visibility_cb(GtkWidget *w, GdkEventVisibility *event, gpointer data) +{ + if (event->state == GDK_VISIBILITY_FULLY_OBSCURED) + gaim_gtk_blist_obscured = TRUE; + else + gaim_gtk_blist_obscured = FALSE; + + /* continue to handle event normally */ + return FALSE; +} + +static void gtk_blist_menu_info_cb(GtkWidget *w, struct buddy *b) +{ + serv_get_info(b->account->gc, b->name); +} + +static void gtk_blist_menu_im_cb(GtkWidget *w, struct buddy *b) +{ + gaim_conversation_new(GAIM_CONV_IM, b->account, b->name); +} + +static void gtk_blist_menu_alias_cb(GtkWidget *w, struct buddy *b) +{ + alias_dialog_bud(b); +} + +static void gtk_blist_menu_bp_cb(GtkWidget *w, struct buddy *b) +{ + gaim_gtkpounce_dialog_show(b, NULL); +} + +static void gtk_blist_menu_showlog_cb(GtkWidget *w, struct buddy *b) +{ + show_log(b->name); +} + +static void gtk_blist_show_systemlog_cb() +{ + show_log(NULL); +} + +static void gtk_blist_show_onlinehelp_cb() +{ + open_url(NULL, WEBSITE "documentation.php"); +} + +static void gtk_blist_button_im_cb(GtkWidget *w, GtkTreeView *tv) +{ + GtkTreeIter iter; + GtkTreeModel *model = gtk_tree_view_get_model(tv); + GtkTreeSelection *sel = gtk_tree_view_get_selection(tv); + + if(gtk_tree_selection_get_selected(sel, &model, &iter)){ + GaimBlistNode *node; + + gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name); + return; + } + } + show_im_dialog(); +} + +static void gtk_blist_button_info_cb(GtkWidget *w, GtkTreeView *tv) +{ + GtkTreeIter iter; + GtkTreeModel *model = gtk_tree_view_get_model(tv); + GtkTreeSelection *sel = gtk_tree_view_get_selection(tv); + + if(gtk_tree_selection_get_selected(sel, &model, &iter)){ + GaimBlistNode *node; + + gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + serv_get_info(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name); + return; + } + } + show_info_dialog(); +} + +static void gtk_blist_button_chat_cb(GtkWidget *w, gpointer data) +{ + /* FIXME: someday, we can check to see if we've selected a chat node */ + join_chat(); +} + +static void gtk_blist_button_away_cb(GtkWidget *w, gpointer data) +{ + gtk_menu_popup(GTK_MENU(awaymenu), NULL, NULL, NULL, NULL, 1, GDK_CURRENT_TIME); +} + +static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) { + GaimBlistNode *node; + GValue val = {0,}; + + gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val); + + node = g_value_get_pointer(&val); + + if (GAIM_BLIST_NODE_IS_GROUP(node)) { + gaim_group_set_setting((struct group *)node, "collapsed", NULL); + gaim_blist_save(); + } +} + +static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data) { + GaimBlistNode *node; + GValue val = {0,}; + + gtk_tree_model_get_value(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &val); + + node = g_value_get_pointer(&val); + + if (GAIM_BLIST_NODE_IS_GROUP(node)) { + gaim_group_set_setting((struct group *)node, "collapsed", "true"); + gaim_blist_save(); + } +} + +static void gtk_blist_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) { + GaimBlistNode *node; + GtkTreeIter iter; + GValue val = { 0, }; + + gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); + + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); + node = g_value_get_pointer(&val); + + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + struct gaim_conversation *conv = + gaim_conversation_new(GAIM_CONV_IM, ((struct buddy*)node)->account, ((struct buddy*)node)->name); + if(conv) { + gaim_window_raise(gaim_conversation_get_window(conv)); + gaim_window_switch_conversation( + gaim_conversation_get_window(conv), + gaim_conversation_get_index(conv)); + } + } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { + if (gtk_tree_view_row_expanded(tv, path)) + gtk_tree_view_collapse_row(tv, path); + else + gtk_tree_view_expand_row(tv,path,FALSE); + } +} + +static void gaim_gtk_blist_add_buddy_cb() +{ + GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); + GtkTreeIter iter; + GaimBlistNode *node; + + if(gtk_tree_selection_get_selected(sel, NULL, &iter)){ + gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1); + if (GAIM_BLIST_NODE_IS_BUDDY(node)) + show_add_buddy(NULL, NULL, ((struct group*)node->parent)->name, NULL); + else if (GAIM_BLIST_NODE_IS_GROUP(node)) + show_add_buddy(NULL, NULL, ((struct group*)node)->name, NULL); + } + else { + show_add_buddy(NULL, NULL, NULL, NULL); + } +} + +static void +gaim_gtk_blist_remove_cb (GtkWidget *w, GaimBlistNode *node) +{ + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + struct buddy *b = (struct buddy*)node; + show_confirm_del(b->account->gc, b->name); + } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { + struct group *g = (struct group*)node; + show_confirm_del_group(g); + } +} + +static void gaim_proto_menu_cb(GtkMenuItem *item, struct buddy *b) +{ + struct proto_buddy_menu *pbm = g_object_get_data(G_OBJECT(item), "gaimcallback"); + if (pbm->callback) + pbm->callback(pbm->gc, b->name); +} + +static gboolean gtk_blist_button_press_cb(GtkWidget *tv, GdkEventButton *event, gpointer null) +{ + GtkTreePath *path; + GaimBlistNode *node; + GValue val = { 0, }; + GtkTreeIter iter; + GtkWidget *menu, *menuitem; + GtkTreeSelection *sel; + GList *list; + GaimPlugin *prpl = NULL; + GaimPluginProtocolInfo *prpl_info = NULL; + + if (event->button != 3) + return FALSE; + + /* Here we figure out which node was clicked */ + if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL)) + return FALSE; + gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); + node = g_value_get_pointer(&val); + menu = gtk_menu_new(); + + if (GAIM_BLIST_NODE_IS_GROUP(node)) { + gaim_new_item_from_stock(menu, _("_Add a Buddy"), GTK_STOCK_ADD, G_CALLBACK(gaim_gtk_blist_add_buddy_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Delete Group"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Rename"), NULL, G_CALLBACK(show_rename_group), node, 0, 0, NULL); + } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + /* Protocol specific options */ + prpl = gaim_find_prpl(((struct buddy*)node)->account->protocol); + + if (prpl != NULL) + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + + if (prpl && prpl_info->get_info) + gaim_new_item_from_stock(menu, _("_Get Info"), GAIM_STOCK_INFO, G_CALLBACK(gtk_blist_menu_info_cb), node, 0, 0, NULL); + + gaim_new_item_from_stock(menu, _("_IM"), GAIM_STOCK_IM, G_CALLBACK(gtk_blist_menu_im_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("Add Buddy _Pounce"), NULL, G_CALLBACK(gtk_blist_menu_bp_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("View _Log"), NULL, G_CALLBACK(gtk_blist_menu_showlog_cb), node, 0, 0, NULL); + + if (prpl) { + list = prpl_info->buddy_menu(((struct buddy*)node)->account->gc, ((struct buddy*)node)->name); + while (list) { + struct proto_buddy_menu *pbm = list->data; + menuitem = gtk_menu_item_new_with_mnemonic(pbm->label); + g_object_set_data(G_OBJECT(menuitem), "gaimcallback", pbm); + g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(gaim_proto_menu_cb), node); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + list = list->next; + } + } + + gaim_event_broadcast (event_draw_menu, menu, ((struct buddy *) node)->name); + + gaim_separator(menu); + gaim_new_item_from_stock(menu, _("_Alias"), GAIM_STOCK_EDIT, G_CALLBACK(gtk_blist_menu_alias_cb), node, 0, 0, NULL); + gaim_new_item_from_stock(menu, _("_Remove"), GTK_STOCK_REMOVE, G_CALLBACK(gaim_gtk_blist_remove_cb), node, 0, 0, NULL); + } + + gtk_widget_show_all(menu); + + gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time); + +#if (1) /* This code only exists because GTK doesn't work. If we return FALSE here, as would be normal + * the event propoagates down and somehow gets interpreted as the start of a drag event. */ + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tv)); + gtk_tree_selection_select_path(sel, path); + gtk_tree_path_free(path); + return TRUE; +#endif +} + +static void gaim_gtk_blist_show_empty_groups_cb(gpointer data, guint action, GtkWidget *item) +{ + if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item))) + blist_options &= ~OPT_BLIST_NO_MT_GRP; + else + blist_options |= OPT_BLIST_NO_MT_GRP; + save_prefs(); + gaim_gtk_blist_refresh(gaim_get_blist()); +} + +static void gaim_gtk_blist_edit_mode_cb(gpointer callback_data, guint callback_action, + GtkWidget *checkitem) { + if(gtkblist->window->window) { + GdkCursor *cursor = gdk_cursor_new(GDK_WATCH); + gdk_window_set_cursor(gtkblist->window->window, cursor); + while (gtk_events_pending()) + gtk_main_iteration(); + gdk_cursor_unref(cursor); + } + + if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(checkitem))) + blist_options |= OPT_BLIST_SHOW_OFFLINE; + else + blist_options &= ~OPT_BLIST_SHOW_OFFLINE; + save_prefs(); + + if(gtkblist->window->window) { + GdkCursor *cursor = gdk_cursor_new(GDK_LEFT_PTR); + gdk_window_set_cursor(gtkblist->window->window, cursor); + gdk_cursor_unref(cursor); + } + + gaim_gtk_blist_refresh(gaim_get_blist()); +} + +static void gaim_gtk_blist_drag_data_get_cb (GtkWidget *widget, + GdkDragContext *dc, + GtkSelectionData *data, + guint info, + guint time, + gpointer *null) +{ + if (data->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE)) { + GtkTreeRowReference *ref = g_object_get_data(G_OBJECT(dc), "gtk-tree-view-source-row"); + GtkTreePath *sourcerow = gtk_tree_row_reference_get_path(ref); + GtkTreeIter iter; + GaimBlistNode *node = NULL; + GValue val = {0}; + gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, sourcerow); + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); + node = g_value_get_pointer(&val); + gtk_selection_data_set (data, + gdk_atom_intern ("GAIM_BLIST_NODE", FALSE), + 8, /* bits */ + (void*)&node, + sizeof (node)); + + gtk_tree_path_free(sourcerow); + } + +} + +static void gaim_gtk_blist_drag_data_rcv_cb(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, + GtkSelectionData *sd, guint info, guint t) +{ + if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE) && sd->data) { + GaimBlistNode *n = NULL; + GtkTreePath *path = NULL; + GtkTreeViewDropPosition position; + memcpy(&n, sd->data, sizeof(n)); + if(gtk_tree_view_get_dest_row_at_pos(GTK_TREE_VIEW(widget), x, y, &path, &position)) { + /* if we're here, I think it means the drop is ok */ + GtkTreeIter iter; + GaimBlistNode *node; + GValue val = {0}; + gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); + node = g_value_get_pointer(&val); + + if (GAIM_BLIST_NODE_IS_BUDDY(n)) { + struct buddy *b = (struct buddy*)n; + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + switch(position) { + case GTK_TREE_VIEW_DROP_AFTER: + case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: + gaim_blist_add_buddy(b, (struct group*)node->parent, node); + break; + case GTK_TREE_VIEW_DROP_BEFORE: + case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: + gaim_blist_add_buddy(b, (struct group*)node->parent, node->prev); + break; + } + } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { + gaim_blist_add_buddy(b, (struct group*)node, NULL); + } + } else if (GAIM_BLIST_NODE_IS_GROUP(n)) { + struct group *g = (struct group*)n; + if (GAIM_BLIST_NODE_IS_GROUP(node)) { + switch (position) { + case GTK_TREE_VIEW_DROP_INTO_OR_AFTER: + case GTK_TREE_VIEW_DROP_AFTER: + gaim_blist_add_group(g, node); + break; + case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE: + case GTK_TREE_VIEW_DROP_BEFORE: + gaim_blist_add_group(g, node->prev); + break; + } + + } else if(GAIM_BLIST_NODE_IS_BUDDY(node)) { + gaim_blist_add_group(g, node->parent); + } + + } + + gtk_tree_path_free(path); + gaim_blist_save(); + } + } +} + +static void gaim_gtk_blist_paint_tip(GtkWidget *widget, GdkEventExpose *event, struct buddy *b) +{ + GtkStyle *style; + GdkPixbuf *pixbuf = gaim_gtk_blist_get_status_icon(b, GAIM_STATUS_ICON_LARGE); + PangoLayout *layout; + char *tooltiptext = gaim_get_tooltip_text(b); + + layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); + pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD); + pango_layout_set_width(layout, 300000); + style = gtkblist->tipwindow->style; + + gtk_paint_flat_box (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, + NULL, gtkblist->tipwindow, "tooltip", 0, 0, -1, -1); + +#if GTK_CHECK_VERSION(2,2,0) + gdk_draw_pixbuf(GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, pixbuf, + 0, 0, 4, 4, -1 , -1, GDK_RGB_DITHER_NONE, 0, 0); +#else + gdk_pixbuf_render_to_drawable(pixbuf, GDK_DRAWABLE(gtkblist->tipwindow->window), NULL, 0, 0, 4, 4, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); +#endif + + gtk_paint_layout (style, gtkblist->tipwindow->window, GTK_STATE_NORMAL, TRUE, + NULL, gtkblist->tipwindow, "tooltip", 38, 4, layout); + + g_object_unref (pixbuf); + g_object_unref (layout); + g_free(tooltiptext); + return; +} + +static gboolean gaim_gtk_blist_tooltip_timeout(GtkWidget *tv) +{ + GtkTreePath *path; + GtkTreeIter iter; + GaimBlistNode *node; + GValue val = {0}; + + if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), gtkblist->rect.x, gtkblist->rect.y, &path, NULL, NULL, NULL)) + return FALSE; + gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkblist->treemodel), &iter, path); + gtk_tree_model_get_value (GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &val); + node = g_value_get_pointer(&val); + + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + int scr_w,scr_h, w, h, x, y; + PangoLayout *layout; + struct buddy *buddy = (struct buddy*)node; + char *tooltiptext = gaim_get_tooltip_text(buddy); + gtkblist->tipwindow = gtk_window_new(GTK_WINDOW_POPUP); + gtkblist->tipwindow->parent = tv; + gtk_widget_set_app_paintable(gtkblist->tipwindow, TRUE); + gtk_window_set_resizable(GTK_WINDOW(gtkblist->tipwindow), FALSE); + gtk_widget_set_name(gtkblist->tipwindow, "gtk-tooltips"); + g_signal_connect(G_OBJECT(gtkblist->tipwindow), "expose_event", + G_CALLBACK(gaim_gtk_blist_paint_tip), buddy); + gtk_widget_ensure_style (gtkblist->tipwindow); + + layout = gtk_widget_create_pango_layout (gtkblist->tipwindow, NULL); + pango_layout_set_wrap(layout, PANGO_WRAP_WORD); + pango_layout_set_width(layout, 300000); + pango_layout_set_markup(layout, tooltiptext, strlen(tooltiptext)); + scr_w = gdk_screen_width(); + scr_h = gdk_screen_height(); + pango_layout_get_size (layout, &w, &h); + w = PANGO_PIXELS(w) + 8; + h = PANGO_PIXELS(h) + 8; + + /* 38 is the size of a large status icon plus 4 pixels padding on each side. + I should #define this or something */ + w = w + 38; + h = MAX(h, 38); + + gdk_window_get_pointer(NULL, &x, &y, NULL); + if (GTK_WIDGET_NO_WINDOW(gtkblist->window)) + y+=gtkblist->window->allocation.y; + + x -= ((w >> 1) + 4); + + if ((x + w) > scr_w) + x -= (x + w) - scr_w; + else if (x < 0) + x = 0; + + if ((y + h + 4) > scr_h) + y = y - h; + else + y = y + 6; + g_object_unref (layout); + g_free(tooltiptext); + gtk_widget_set_size_request(gtkblist->tipwindow, w, h); + gtk_window_move(GTK_WINDOW(gtkblist->tipwindow), x, y); + gtk_widget_show(gtkblist->tipwindow); + } + + gtk_tree_path_free(path); + return FALSE; +} + +static gboolean gaim_gtk_blist_motion_cb (GtkWidget *tv, GdkEventMotion *event, gpointer null) +{ + GtkTreePath *path; + if (gtkblist->timeout) { + if ((event->y > gtkblist->rect.y) && ((event->y - gtkblist->rect.height) < gtkblist->rect.y)) + return FALSE; + /* We've left the cell. Remove the timeout and create a new one below */ + if (gtkblist->tipwindow) { + gtk_widget_destroy(gtkblist->tipwindow); + gtkblist->tipwindow = NULL; + } + + g_source_remove(gtkblist->timeout); + } + + gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tv), event->x, event->y, &path, NULL, NULL, NULL); + gtk_tree_view_get_cell_area(GTK_TREE_VIEW(tv), path, NULL, >kblist->rect); + if (path) + gtk_tree_path_free(path); + gtkblist->timeout = g_timeout_add(500, (GSourceFunc)gaim_gtk_blist_tooltip_timeout, tv); + return FALSE; +} + +static void gaim_gtk_blist_leave_cb (GtkWidget *w, GdkEventCrossing *e, gpointer n) +{ + if (gtkblist->timeout) { + g_source_remove(gtkblist->timeout); + gtkblist->timeout = 0; + } + if (gtkblist->tipwindow) { + gtk_widget_destroy(gtkblist->tipwindow); + gtkblist->tipwindow = NULL; + } +} + +static void +toggle_debug(void) +{ + misc_options ^= OPT_MISC_DEBUG; + + if ((misc_options & OPT_MISC_DEBUG)) + gaim_gtk_debug_window_show(); + else + gaim_gtk_debug_window_hide(); + + save_prefs(); +} + + +/*************************************************** + * Crap * + ***************************************************/ +static GtkItemFactoryEntry blist_menu[] = +{ + /* Buddies menu */ + { N_("/_Buddies"), NULL, NULL, 0, "" }, + { N_("/Buddies/New _Instant Message..."), "I", show_im_dialog, 0, "", GAIM_STOCK_IM }, + { N_("/Buddies/Join a _Chat..."), "C", join_chat, 0, "", GAIM_STOCK_CHAT }, + { N_("/Buddies/Get _User Info..."), "J", show_info_dialog, 0, "", GAIM_STOCK_INFO }, + { "/Buddies/sep1", NULL, NULL, 0, "" }, + { N_("/Buddies/_Show Offline Buddies"), NULL, gaim_gtk_blist_edit_mode_cb, 1, ""}, + { N_("/Buddies/Show _Empty Groups"), NULL, gaim_gtk_blist_show_empty_groups_cb, 1, ""}, + { N_("/Buddies/_Add a Buddy..."), NULL, gaim_gtk_blist_add_buddy_cb, 0, "", GTK_STOCK_ADD }, + { N_("/Buddies/Add a _Group..."), NULL, show_add_group, 0, NULL}, + { "/Buddies/sep2", NULL, NULL, 0, "" }, + { N_("/Buddies/_Signoff"), "D", signoff_all, 0, "", GAIM_STOCK_SIGN_OFF }, + { N_("/Buddies/_Quit"), "Q", do_quit, 0, "", GTK_STOCK_QUIT }, + + /* Tools */ + { N_("/_Tools"), NULL, NULL, 0, "" }, + { N_("/Tools/_Away"), NULL, NULL, 0, "" }, + { N_("/Tools/Buddy _Pounce"), NULL, NULL, 0, "" }, + { N_("/Tools/P_rotocol Actions"), NULL, NULL, 0, "" }, + { "/Tools/sep1", NULL, NULL, 0, "" }, + { N_("/Tools/A_ccounts..."), "A", account_editor, 0, "", GAIM_STOCK_ACCOUNTS }, + { N_("/Tools/_File Transfers..."), NULL, gaim_show_xfer_dialog, 0, "", GAIM_STOCK_FILE_TRANSFER }, + { N_("/Tools/Preferences..."), "P", show_prefs, 0, "", GTK_STOCK_PREFERENCES }, + { N_("/Tools/Pr_ivacy..."), NULL, show_privacy_options, 0, "", GAIM_STOCK_PRIVACY }, + { "/Tools/sep2", NULL, NULL, 0, "" }, + { N_("/Tools/View System _Log..."), NULL, gtk_blist_show_systemlog_cb, 0, NULL }, + + /* Help */ + { N_("/_Help"), NULL, NULL, 0, "" }, + { N_("/Help/Online _Help"), "F1", gtk_blist_show_onlinehelp_cb, 0, "", GTK_STOCK_HELP }, + { N_("/Help/_Debug Window..."), NULL, toggle_debug, 0, NULL }, + { N_("/Help/_About..."), NULL, show_about, 0, "", GAIM_STOCK_ABOUT }, +}; + +/********************************************************* + * Private Utility functions * + *********************************************************/ + +static char *gaim_get_tooltip_text(struct buddy *b) +{ + GaimPlugin *prpl; + GaimPluginProtocolInfo *prpl_info = NULL; + char *text = NULL; + char *statustext = NULL; + char *aliastext = NULL, *nicktext = NULL; + char *warning = NULL, *idletime = NULL; + + prpl = gaim_find_prpl(b->account->protocol); + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + + if (prpl_info->tooltip_text) { + const char *end; + statustext = prpl_info->tooltip_text(b); + + if(statustext && !g_utf8_validate(statustext, -1, &end)) { + char *new = g_strndup(statustext, + g_utf8_pointer_to_offset(statustext, end)); + g_free(statustext); + statustext = new; + } + } + + if (!statustext && !GAIM_BUDDY_IS_ONLINE(b)) + statustext = g_strdup(_("Status: Offline")); + + if (b->idle > 0) { + int ihrs, imin; + time_t t; + time(&t); + ihrs = (t - b->idle) / 3600; + imin = ((t - b->idle) / 60) % 60; + if (ihrs) + idletime = g_strdup_printf(_("%dh%02dm"), ihrs, imin); + else + idletime = g_strdup_printf(_("%dm"), imin); + } + + if(b->alias && b->alias[0]) + aliastext = g_markup_escape_text(b->alias, -1); + + if(b->server_alias) + nicktext = g_markup_escape_text(b->server_alias, -1); + + if (b->evil > 0) + warning = g_strdup_printf(_("%d%%"), b->evil); + + text = g_strdup_printf("%s" + "%s %s" /* Alias */ + "%s %s" /* Nickname */ + "%s %s" /* Idle */ + "%s %s" /* Warning */ + "%s%s" /* Status */ + "%s", + b->name, + aliastext ? _("\nAlias:") : "", aliastext ? aliastext : "", + nicktext ? _("\nNickname:") : "", nicktext ? nicktext : "", + idletime ? _("\nIdle:") : "", idletime ? idletime : "", + b->evil ? _("\nWarned:") : "", b->evil ? warning : "", + statustext ? "\n" : "", statustext ? statustext : "", + !g_ascii_strcasecmp(b->name, "robflynn") ? _("\nDescription: Spooky") : ""); + + if(warning) + g_free(warning); + if(idletime) + g_free(idletime); + if(statustext) + g_free(statustext); + if(nicktext) + g_free(nicktext); + if(aliastext) + g_free(aliastext); + + return text; + +} + +GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b, GaimStatusIconSize size) +{ + GdkPixbuf *status = NULL; + GdkPixbuf *scale = NULL; + GdkPixbuf *emblem = NULL; + gchar *filename = NULL; + const char *protoname = NULL; + + char *se = NULL, *sw = NULL ,*nw = NULL ,*ne = NULL; + + int scalesize = 30; + + GaimPlugin *prpl; + GaimPluginProtocolInfo *prpl_info = NULL; + + prpl = gaim_find_prpl(b->account->protocol); + + if (!prpl) + return NULL; + + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + + if (prpl_info->list_icon) + protoname = prpl_info->list_icon(b->account, b); + if (b->present != GAIM_BUDDY_SIGNING_OFF && prpl_info->list_emblems) + prpl_info->list_emblems(b, &se, &sw, &nw, &ne); + + if (size == GAIM_STATUS_ICON_SMALL) { + scalesize = 15; + sw = nw = ne = NULL; /* So that only the se icon will composite */ + } + + + if (b->present == GAIM_BUDDY_SIGNING_ON) { + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "login.png", NULL); + status = gdk_pixbuf_new_from_file(filename,NULL); + g_free(filename); + } else if (b->present == GAIM_BUDDY_SIGNING_OFF) { + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "logout.png", NULL); + status = gdk_pixbuf_new_from_file(filename,NULL); + g_free(filename); + + /* "Hey, what's all this crap?" you ask. Status icons will be themeable too, and + then it will look up protoname from the theme */ + } else { + char *image = g_strdup_printf("%s.png", protoname); + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); + status = gdk_pixbuf_new_from_file(filename,NULL); + g_free(image); + g_free(filename); + + } + + if (!status) + return NULL; + + scale = gdk_pixbuf_scale_simple(status, scalesize, scalesize, GDK_INTERP_BILINEAR); + + g_object_unref(G_OBJECT(status)); + + /* Emblems */ + + /* Each protocol can specify up to four "emblems" to composite over the base icon. "away", "busy", "mobile user" + * are all examples of states represented by emblems. I'm not even really sure I like this yet. */ + + /* XXX Clean this crap up, yo. */ + if (se) { + char *image = g_strdup_printf("%s.png", se); + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); + g_free(image); + emblem = gdk_pixbuf_new_from_file(filename,NULL); + g_free(filename); + if (emblem) { + if (size == GAIM_STATUS_ICON_LARGE) + gdk_pixbuf_composite (emblem, + scale, 15, 15, + 15, 15, + 15, 15, + 1, 1, + GDK_INTERP_BILINEAR, + 255); + else + gdk_pixbuf_composite (emblem, + scale, 5, 5, + 10, 10, + 5, 5, + .6, .6, + GDK_INTERP_BILINEAR, + 255); + g_object_unref(G_OBJECT(emblem)); + } + } + if (sw) { + char *image = g_strdup_printf("%s.png", sw); + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); + g_free(image); + emblem = gdk_pixbuf_new_from_file(filename,NULL); + g_free(filename); + if (emblem) { + gdk_pixbuf_composite (emblem, + scale, 0, 15, + 15, 15, + 0, 15, + 1, 1, + GDK_INTERP_BILINEAR, + 255); + g_object_unref(G_OBJECT(emblem)); + } + } + if (nw) { + char *image = g_strdup_printf("%s.png", nw); + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); + g_free(image); + emblem = gdk_pixbuf_new_from_file(filename,NULL); + g_free(filename); + if (emblem) { + gdk_pixbuf_composite (emblem, + scale, 0, 0, + 15, 15, + 0, 0, + 1, 1, + GDK_INTERP_BILINEAR, + 255); + g_object_unref(G_OBJECT(emblem)); + } + } + if (ne) { + char *image = g_strdup_printf("%s.png", ne); + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); + g_free(image); + emblem = gdk_pixbuf_new_from_file(filename,NULL); + g_free(filename); + if (emblem) { + gdk_pixbuf_composite (emblem, + scale, 15, 0, + 15, 15, + 15, 0, + 1, 1, + GDK_INTERP_BILINEAR, + 255); + g_object_unref(G_OBJECT(emblem)); + } + } + + + /* Idle grey buddies affects the whole row. This converts the status icon to greyscale. */ + if (b->present == GAIM_BUDDY_OFFLINE) + gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.0, FALSE); + else if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS) + gdk_pixbuf_saturate_and_pixelate(scale, scale, 0.25, FALSE); + return scale; +} + +static GdkPixbuf *gaim_gtk_blist_get_buddy_icon(struct buddy *b) +{ + /* This just opens a file from ~/.gaim/icons/screenname. This needs to change to be more gooder. */ + char *file; + GdkPixbuf *buf, *ret; + + if (!(blist_options & OPT_BLIST_SHOW_ICONS)) + return NULL; + + if ((file = gaim_buddy_get_setting(b, "buddy_icon")) == NULL) + return NULL; + + buf = gdk_pixbuf_new_from_file(file, NULL); + g_free(file); + + + if (buf) { + if (!GAIM_BUDDY_IS_ONLINE(b)) + gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.0, FALSE); + if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS) + gdk_pixbuf_saturate_and_pixelate(buf, buf, 0.25, FALSE); + + ret = gdk_pixbuf_scale_simple(buf,30,30, GDK_INTERP_BILINEAR); + g_object_unref(G_OBJECT(buf)); + return ret; + } + return NULL; +} + +static gchar *gaim_gtk_blist_get_name_markup(struct buddy *b, gboolean selected) +{ + char *name = gaim_get_buddy_alias(b); + char *esc = g_markup_escape_text(name, strlen(name)), *text = NULL; + GaimPlugin *prpl; + GaimPluginProtocolInfo *prpl_info = NULL; + /* XXX Clean up this crap */ + + int ihrs, imin; + char *idletime = NULL, *warning = NULL, *statustext = NULL; + time_t t; + + prpl = gaim_find_prpl(b->account->protocol); + + if (prpl != NULL) + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + + if (!(blist_options & OPT_BLIST_SHOW_ICONS)) { + if ((b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) || !GAIM_BUDDY_IS_ONLINE(b)) { + text = g_strdup_printf("%s", + esc); + g_free(esc); + return text; + } else { + return esc; + } + } + + time(&t); + ihrs = (t - b->idle) / 3600; + imin = ((t - b->idle) / 60) % 60; + + if (prpl && prpl_info->status_text) { + char *tmp = prpl_info->status_text(b); + const char *end; + + if(tmp && !g_utf8_validate(tmp, -1, &end)) { + char *new = g_strndup(tmp, + g_utf8_pointer_to_offset(tmp, end)); + g_free(tmp); + tmp = new; + } + + if(tmp) { + char buf[32]; + char *c = tmp; + int length = 0, vis=0; + gboolean inside = FALSE; + g_strdelimit(tmp, "\n", ' '); + + while(*c && vis < 20) { + if(*c == '&') + inside = TRUE; + else if(*c == ';') + inside = FALSE; + if(!inside) + vis++; + length++; + c++; /* this is fun */ + } + + if(vis == 20) + g_snprintf(buf, sizeof(buf), "%%.%ds...", length); + else + g_snprintf(buf, sizeof(buf), "%%s "); + + statustext = g_strdup_printf(buf, tmp); + + g_free(tmp); + } + } + + if (b->idle > 0) { + if (ihrs) + idletime = g_strdup_printf(_("Idle (%dh%02dm) "), ihrs, imin); + else + idletime = g_strdup_printf(_("Idle (%dm) "), imin); + } + + if (b->evil > 0) + warning = g_strdup_printf(_("Warned (%d%%) "), b->evil); + + if(!GAIM_BUDDY_IS_ONLINE(b) && !statustext) + statustext = g_strdup("Offline "); + + if (b->idle && blist_options & OPT_BLIST_GREY_IDLERS && !selected) { + text = g_strdup_printf("%s\n" + "%s%s%s", + esc, + statustext != NULL ? statustext : "", + idletime != NULL ? idletime : "", + warning != NULL ? warning : ""); + } else if (statustext == NULL && idletime == NULL && warning == NULL && GAIM_BUDDY_IS_ONLINE(b)) { + text = g_strdup(esc); + } else { + text = g_strdup_printf("%s\n" + "%s%s%s", esc, + selected ? "" : "color='dim grey'", + statustext != NULL ? statustext : "", + idletime != NULL ? idletime : "", + warning != NULL ? warning : ""); + } + if (idletime) + g_free(idletime); + if (warning) + g_free(warning); + if (statustext) + g_free(statustext); + if (esc) + g_free(esc); + + return text; +} + +static void gaim_gtk_blist_restore_position() +{ + /* if the window exists, is hidden, we're saving positions, and the position is sane... */ + if(gtkblist && gtkblist->window && + !GTK_WIDGET_VISIBLE(gtkblist->window) && + blist_pos.width != 0) { + /* ...check position is on screen... */ + if (blist_pos.x >= gdk_screen_width()) + blist_pos.x = gdk_screen_width() - 100; + else if (blist_pos.x < 0) + blist_pos.x = 100; + + if (blist_pos.y >= gdk_screen_height()) + blist_pos.y = gdk_screen_height() - 100; + else if (blist_pos.y < 0) + blist_pos.y = 100; + + /* ...and move it back. */ + gtk_window_move(GTK_WINDOW(gtkblist->window), blist_pos.x, blist_pos.y); + gtk_window_resize(GTK_WINDOW(gtkblist->window), blist_pos.width, blist_pos.height); + } +} + +static gboolean gaim_gtk_blist_refresh_timer(struct gaim_buddy_list *list) +{ + GaimBlistNode *group = list->root; + GaimBlistNode *buddy; + + while (group) { + buddy = group->child; + while (buddy) { + if (((struct buddy *)buddy)->idle) + gaim_gtk_blist_update(list, buddy); + buddy = buddy->next; + } + group = group->next; + } + + /* keep on going */ + return TRUE; +} + +/********************************************************************************** + * Public API Functions * + **********************************************************************************/ +static void gaim_gtk_blist_new_list(struct gaim_buddy_list *blist) +{ + blist->ui_data = g_new0(struct gaim_gtk_buddy_list, 1); +} + +void gaim_gtk_blist_update_columns() +{ + if(!gtkblist) + return; + + if (blist_options & OPT_BLIST_SHOW_ICONS) { + gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, TRUE); + gtk_tree_view_column_set_visible(gtkblist->idle_column, FALSE); + gtk_tree_view_column_set_visible(gtkblist->warning_column, FALSE); + } else { + gtk_tree_view_column_set_visible(gtkblist->idle_column, blist_options & OPT_BLIST_SHOW_IDLETIME); + gtk_tree_view_column_set_visible(gtkblist->warning_column, blist_options & OPT_BLIST_SHOW_WARN); + gtk_tree_view_column_set_visible(gtkblist->buddy_icon_column, FALSE); + } +} + +enum {DRAG_BUDDY, DRAG_ROW}; + +static char * +item_factory_translate_func (const char *path, gpointer func_data) +{ + return _(path); +} + +static void gaim_gtk_blist_show(struct gaim_buddy_list *list) +{ + GtkItemFactory *ift; + GtkCellRenderer *rend; + GtkTreeViewColumn *column; + GtkWidget *sw; + GtkWidget *button; + GtkSizeGroup *sg; + GtkAccelGroup *accel_group; + GtkTreeSelection *selection; + GtkTargetEntry gte[] = {{"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, DRAG_ROW}, + {"application/x-im-contact", 0, DRAG_BUDDY}}; + + if (gtkblist && gtkblist->window) { + gtk_widget_show(gtkblist->window); + return; + } + + gtkblist = GAIM_GTK_BLIST(list); + + gtkblist->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_role(GTK_WINDOW(gtkblist->window), "buddy_list"); + gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List")); + + GTK_WINDOW(gtkblist->window)->allow_shrink = TRUE; + + gtkblist->vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->vbox); + + g_signal_connect(G_OBJECT(gtkblist->window), "delete_event", G_CALLBACK(gtk_blist_delete_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->window), "configure_event", G_CALLBACK(gtk_blist_configure_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->window), "visibility_notify_event", G_CALLBACK(gtk_blist_visibility_cb), NULL); + gtk_widget_add_events(gtkblist->window, GDK_VISIBILITY_NOTIFY_MASK); + + /******************************* Menu bar *************************************/ + accel_group = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW (gtkblist->window), accel_group); + g_object_unref(accel_group); + ift = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "", accel_group); + gtk_item_factory_set_translate_func (ift, + item_factory_translate_func, + NULL, NULL); + gtk_item_factory_create_items(ift, sizeof(blist_menu) / sizeof(*blist_menu), + blist_menu, NULL); + gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtk_item_factory_get_widget(ift, ""), FALSE, FALSE, 0); + + awaymenu = gtk_item_factory_get_widget(ift, N_("/Tools/Away")); + do_away_menu(); + + gtkblist->bpmenu = gtk_item_factory_get_widget(ift, N_("/Tools/Buddy Pounce")); + gaim_gtkpounce_menu_build(gtkblist->bpmenu); + + protomenu = gtk_item_factory_get_widget(ift, N_("/Tools/Protocol Actions")); + do_proto_menu(); + + /****************************** GtkTreeView **********************************/ + sw = gtk_scrolled_window_new(NULL,NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + gtkblist->treemodel = gtk_tree_store_new(BLIST_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_POINTER); + + gtkblist->treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(gtkblist->treemodel)); + gtk_widget_set_size_request(gtkblist->treeview, -1, 200); + + /* Set up selection stuff */ + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkblist->treeview)); + g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(gaim_gtk_blist_selection_changed), NULL); + + + /* Set up dnd */ + gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(gtkblist->treeview), GDK_BUTTON1_MASK, gte, + 2, GDK_ACTION_COPY); + gtk_tree_view_enable_model_drag_dest(GTK_TREE_VIEW(gtkblist->treeview), gte, 2, + GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-received", G_CALLBACK(gaim_gtk_blist_drag_data_rcv_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->treeview), "drag-data-get", G_CALLBACK(gaim_gtk_blist_drag_data_get_cb), NULL); + + /* Tooltips */ + g_signal_connect(G_OBJECT(gtkblist->treeview), "motion-notify-event", G_CALLBACK(gaim_gtk_blist_motion_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->treeview), "leave-notify-event", G_CALLBACK(gaim_gtk_blist_leave_cb), NULL); + + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkblist->treeview), FALSE); + + column = gtk_tree_view_column_new (); + + rend = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start (column, rend, FALSE); + gtk_tree_view_column_set_attributes (column, rend, + "pixbuf", STATUS_ICON_COLUMN, + "visible", STATUS_ICON_VISIBLE_COLUMN, + NULL); + g_object_set(rend, "xalign", 0.0, "ypad", 0, NULL); + + rend = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start (column, rend, TRUE); + gtk_tree_view_column_set_attributes (column, rend, + "markup", NAME_COLUMN, + NULL); + g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL); + + gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), column); + + rend = gtk_cell_renderer_text_new(); + gtkblist->warning_column = gtk_tree_view_column_new_with_attributes("Warning", rend, "markup", WARNING_COLUMN, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->warning_column); + g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); + + rend = gtk_cell_renderer_text_new(); + gtkblist->idle_column = gtk_tree_view_column_new_with_attributes("Idle", rend, "markup", IDLE_COLUMN, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->idle_column); + g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); + + rend = gtk_cell_renderer_pixbuf_new(); + gtkblist->buddy_icon_column = gtk_tree_view_column_new_with_attributes("Buddy Icon", rend, "pixbuf", BUDDY_ICON_COLUMN, NULL); + g_object_set(rend, "xalign", 1.0, "ypad", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->buddy_icon_column); + + g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", G_CALLBACK(gtk_blist_row_collapsed_cb), NULL); + g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL); + + gtk_box_pack_start(GTK_BOX(gtkblist->vbox), sw, TRUE, TRUE, 0); + gtk_container_add(GTK_CONTAINER(sw), gtkblist->treeview); + gaim_gtk_blist_update_columns(); + + /* set the Show Offline Buddies option. must be done + * after the treeview or faceprint gets mad. -Robot101 + */ + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (ift, N_("/Buddies/Show Offline Buddies"))), + blist_options & OPT_BLIST_SHOW_OFFLINE); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtk_item_factory_get_item (ift, N_("/Buddies/Show Empty Groups"))), + !(blist_options & OPT_BLIST_NO_MT_GRP)); + + /* OK... let's show this bad boy. */ + gaim_gtk_blist_refresh(list); + gaim_gtk_blist_restore_position(); + gtk_widget_show_all(gtkblist->window); + + /**************************** Button Box **************************************/ + /* add this afterwards so it doesn't force up the width of the window */ + + gtkblist->tooltips = gtk_tooltips_new(); + + sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); + gtkblist->bbox = gtk_hbox_new(TRUE, 0); + gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtkblist->bbox, FALSE, FALSE, 0); + gtk_widget_show(gtkblist->bbox); + + button = gaim_pixbuf_button_from_stock(_("IM"), GAIM_STOCK_IM, GAIM_BUTTON_VERTICAL); + gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_size_group_add_widget(sg, button); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_im_cb), + gtkblist->treeview); + gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Send a message to the selected buddy"), NULL); + gtk_widget_show(button); + + button = gaim_pixbuf_button_from_stock(_("Get Info"), GAIM_STOCK_INFO, GAIM_BUTTON_VERTICAL); + gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_size_group_add_widget(sg, button); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_info_cb), + gtkblist->treeview); + gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Get information on the selected buddy"), NULL); + gtk_widget_show(button); + + button = gaim_pixbuf_button_from_stock(_("Chat"), GAIM_STOCK_CHAT, GAIM_BUTTON_VERTICAL); + gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_size_group_add_widget(sg, button); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_chat_cb), NULL); + gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Join a chat room"), NULL); + gtk_widget_show(button); + + button = gaim_pixbuf_button_from_stock(_("Away"), GAIM_STOCK_ICON_AWAY, GAIM_BUTTON_VERTICAL); + gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0); + gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); + gtk_size_group_add_widget(sg, button); + g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_away_cb), NULL); + gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Set an away message"), NULL); + gtk_widget_show(button); + + /* this will show the right image/label widgets for us */ + gaim_gtk_blist_update_toolbar(); + + /* start the refresh timer */ + gtkblist->refresh_timer = g_timeout_add(30000, (GSourceFunc)gaim_gtk_blist_refresh_timer, list); +} + +void gaim_gtk_blist_refresh(struct gaim_buddy_list *list) +{ + GaimBlistNode *group = list->root; + GaimBlistNode *buddy; + + while (group) { + buddy = group->child; + gaim_gtk_blist_update(list, group); + while (buddy) { + gaim_gtk_blist_update(list, buddy); + buddy = buddy->next; + } + group = group->next; + } +} + +static gboolean get_iter_from_node_helper(GaimBlistNode *node, GtkTreeIter *iter, GtkTreeIter *root) { + + do { + GaimBlistNode *n; + GtkTreeIter child; + + gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), root, NODE_COLUMN, &n, -1); + if(n == node) { + *iter = *root; + return TRUE; + } + + if(gtk_tree_model_iter_children(GTK_TREE_MODEL(gtkblist->treemodel), &child, root)) { + if(get_iter_from_node_helper(node,iter,&child)) + return TRUE; + } + } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(gtkblist->treemodel), root)); + + return FALSE; +} + +static gboolean get_iter_from_node(GaimBlistNode *node, GtkTreeIter *iter) { + GtkTreeIter root; + + if (!gtkblist) + return FALSE; + + if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(gtkblist->treemodel), &root)) + return FALSE; + + return get_iter_from_node_helper(node, iter, &root); +} + +/* + * These state assignments suck. I'm sorry. They're for historical reasons. + * Roll on new prefs. -Robot101 + * + * NO_BUTTON_TEXT && SHOW_BUTTON_XPM - image + * !NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - text + * !NO_BUTTON_TEXT && SHOW_BUTTON_XPM - text & images + * NO_BUTTON_TEXT && !SHOW_BUTTON_XPM - none + */ + +static void gaim_gtk_blist_update_toolbar_icons (GtkWidget *widget, gpointer data) { + if (GTK_IS_IMAGE(widget)) { + if (blist_options & OPT_BLIST_SHOW_BUTTON_XPM) + gtk_widget_show(widget); + else + gtk_widget_hide(widget); + } else if (GTK_IS_LABEL(widget)) { + if (blist_options & OPT_BLIST_NO_BUTTON_TEXT) + gtk_widget_hide(widget); + else + gtk_widget_show(widget); + } else if (GTK_IS_CONTAINER(widget)) { + gtk_container_foreach(GTK_CONTAINER(widget), gaim_gtk_blist_update_toolbar_icons, NULL); + } +} + +void gaim_gtk_blist_update_toolbar() { + if (!gtkblist) + return; + + if (blist_options & OPT_BLIST_NO_BUTTON_TEXT && !(blist_options & OPT_BLIST_SHOW_BUTTON_XPM)) + gtk_widget_hide(gtkblist->bbox); + else { + gtk_container_foreach(GTK_CONTAINER(gtkblist->bbox), gaim_gtk_blist_update_toolbar_icons, NULL); + gtk_widget_show(gtkblist->bbox); + } +} + +static void gaim_gtk_blist_remove(struct gaim_buddy_list *list, GaimBlistNode *node) +{ + GtkTreeIter iter; + + /* For some reason, we're called before we have a buddy list sometimes */ + if(!gtkblist) + return; + + if(gtkblist->selected_node == node) + gtkblist->selected_node = NULL; + + if (get_iter_from_node(node, &iter)) { + gtk_tree_store_remove(gtkblist->treemodel, &iter); + if(GAIM_BLIST_NODE_IS_BUDDY(node)) { + gaim_gtk_blist_update(list, node->parent); + } + } +} + +static gboolean do_selection_changed(GaimBlistNode *new_selection) +{ + GaimBlistNode *old_selection = gtkblist->selected_node; + + if(new_selection != gtkblist->selected_node) { + gtkblist->selected_node = new_selection; + if(new_selection) + gaim_gtk_blist_update(NULL, new_selection); + if(old_selection) + gaim_gtk_blist_update(NULL, old_selection); + } + + return FALSE; +} + +static void gaim_gtk_blist_selection_changed(GtkTreeSelection *selection, gpointer data) +{ + GaimBlistNode *new_selection = NULL; + GtkTreeIter iter; + + if(gtk_tree_selection_get_selected(selection, NULL, &iter)){ + gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, + NODE_COLUMN, &new_selection, -1); + } + /* we set this up as a timeout, otherwise the blist flickers */ + g_timeout_add(0, (GSourceFunc)do_selection_changed, new_selection); +} + +static void make_a_group(GaimBlistNode *node, GtkTreeIter *iter) { + GaimBlistNode *sibling; + GtkTreeIter siblingiter; + struct group *group = (struct group *)node; + char *esc = g_markup_escape_text(group->name, -1); + char *mark; + + if(blist_options & OPT_BLIST_SHOW_GRPNUM) + mark = g_strdup_printf("%s (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE)); + else + mark = g_strdup_printf("%s", esc); + + g_free(esc); + + sibling = node->prev; + while (sibling && !get_iter_from_node(sibling, &siblingiter)) { + sibling = sibling->prev; + } + + gtk_tree_store_insert_after(gtkblist->treemodel, iter, NULL, + sibling ? &siblingiter : NULL); + gtk_tree_store_set(gtkblist->treemodel, iter, + STATUS_ICON_COLUMN, NULL, + STATUS_ICON_VISIBLE_COLUMN, FALSE, + NAME_COLUMN, mark, + NODE_COLUMN, node, + -1); + g_free(mark); +} + + +static void gaim_gtk_blist_update(struct gaim_buddy_list *list, GaimBlistNode *node) +{ + GtkTreeIter iter; + GtkTreePath *expand = NULL; + gboolean new_entry = FALSE; + + if (!gtkblist) + return; + + if (!get_iter_from_node(node, &iter)) { /* This is a newly added node */ + new_entry = TRUE; + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + if (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc)) { + GtkTreeIter groupiter; + GaimBlistNode *oldersibling; + GtkTreeIter oldersiblingiter; + char *collapsed = gaim_group_get_setting((struct group *)node->parent, "collapsed"); + + if(node->parent && + !get_iter_from_node(node->parent, &groupiter)) { + /* This buddy's group has not yet been added. + * We do that here */ + make_a_group(node->parent, &groupiter); + } + if(!collapsed) + expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &groupiter); + else + g_free(collapsed); + + oldersibling = node->prev; + while (oldersibling && !get_iter_from_node(oldersibling, &oldersiblingiter)) { + oldersibling = oldersibling->prev; + } + + gtk_tree_store_insert_after(gtkblist->treemodel, &iter, &groupiter, oldersibling ? &oldersiblingiter : NULL); + + if (blist_options & OPT_BLIST_POPUP) { + gtk_widget_show(gtkblist->window); + gtk_window_deiconify(GTK_WINDOW(gtkblist->window)); + gdk_window_raise(gtkblist->window->window); + } + + } + } + else if (GAIM_BLIST_NODE_IS_GROUP(node) && + ((blist_options & OPT_BLIST_SHOW_OFFLINE) || + !(blist_options & OPT_BLIST_NO_MT_GRP))) { + make_a_group(node, &iter); + expand = gtk_tree_model_get_path(GTK_TREE_MODEL(gtkblist->treemodel), &iter); + } + } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { + if((blist_options & OPT_BLIST_NO_MT_GRP) && !(blist_options & OPT_BLIST_SHOW_OFFLINE) && !gaim_blist_get_group_online_count((struct group *)node)) { + gtk_tree_store_remove(gtkblist->treemodel, &iter); + } else { + struct group *group = (struct group *)node; + char *esc = g_markup_escape_text(group->name, -1); + char *mark; + + if(blist_options & OPT_BLIST_SHOW_GRPNUM) + mark = g_strdup_printf("%s (%d/%d)", esc, gaim_blist_get_group_online_count(group), gaim_blist_get_group_size(group, FALSE)); + else + mark = g_strdup_printf("%s", esc); + + g_free(esc); + gtk_tree_store_set(gtkblist->treemodel, &iter, + NAME_COLUMN, mark, + -1); + g_free(mark); + } + } + + if (GAIM_BLIST_NODE_IS_BUDDY(node) && (((struct buddy*)node)->present != GAIM_BUDDY_OFFLINE || ((blist_options & OPT_BLIST_SHOW_OFFLINE) && ((struct buddy*)node)->account->gc))) { + GdkPixbuf *status, *avatar; + char *mark; + char *warning = NULL, *idle = NULL; + + gboolean selected = (gtkblist->selected_node == node); + + status = gaim_gtk_blist_get_status_icon((struct buddy*)node, + blist_options & OPT_BLIST_SHOW_ICONS ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL); + avatar = gaim_gtk_blist_get_buddy_icon((struct buddy*)node); + mark = gaim_gtk_blist_get_name_markup((struct buddy*)node, selected); + + if (((struct buddy*)node)->idle > 0) { + time_t t; + int ihrs, imin; + time(&t); + ihrs = (t - ((struct buddy *)node)->idle) / 3600; + imin = ((t - ((struct buddy*)node)->idle) / 60) % 60; + if(ihrs > 0) + idle = g_strdup_printf("(%d:%02d)", ihrs, imin); + else + idle = g_strdup_printf("(%d)", imin); + } + + if (((struct buddy*)node)->evil > 0) + warning = g_strdup_printf("%d%%", ((struct buddy*)node)->evil); + + + if((blist_options & OPT_BLIST_GREY_IDLERS) + && ((struct buddy *)node)->idle) { + if(warning && !selected) { + char *w2 = g_strdup_printf("%s", + warning); + g_free(warning); + warning = w2; + } + + if(idle && !selected) { + char *i2 = g_strdup_printf("%s", + idle); + g_free(idle); + idle = i2; + } + } + + gtk_tree_store_set(gtkblist->treemodel, &iter, + STATUS_ICON_COLUMN, status, + STATUS_ICON_VISIBLE_COLUMN, TRUE, + NAME_COLUMN, mark, + WARNING_COLUMN, warning, + IDLE_COLUMN, idle, + BUDDY_ICON_COLUMN, avatar, + NODE_COLUMN, node, + -1); + + if (blist_options & OPT_BLIST_POPUP && + ((struct buddy *)node)->present == GAIM_BUDDY_SIGNING_OFF) { + gtk_widget_show(gtkblist->window); + gtk_window_deiconify(GTK_WINDOW(gtkblist->window)); + gdk_window_raise(gtkblist->window->window); + } + + g_free(mark); + if (idle) + g_free(idle); + if (warning) + g_free(warning); + + if (status != NULL) + g_object_unref(status); + + if (avatar != NULL) + g_object_unref(avatar); + + } else if (GAIM_BLIST_NODE_IS_BUDDY(node) && !new_entry) { + gaim_gtk_blist_remove(list, node); + } + gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview)); + + + if(expand) { + gtk_tree_view_expand_row(GTK_TREE_VIEW(gtkblist->treeview), expand, TRUE); + gtk_tree_path_free(expand); + } + + if(GAIM_BLIST_NODE_IS_BUDDY(node)) + gaim_gtk_blist_update(list, node->parent); +} + +static void gaim_gtk_blist_destroy(struct gaim_buddy_list *list) +{ + if (!gtkblist) + return; + + gtk_widget_destroy(gtkblist->window); + gtk_object_sink(GTK_OBJECT(gtkblist->tooltips)); + + if (gtkblist->refresh_timer) + g_source_remove(gtkblist->refresh_timer); + if (gtkblist->timeout) + g_source_remove(gtkblist->timeout); + + gtkblist->refresh_timer = 0; + gtkblist->timeout = 0; + gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL; + gtkblist->treemodel = NULL; + gtkblist->idle_column = NULL; + gtkblist->warning_column = gtkblist->buddy_icon_column = NULL; + gtkblist->bbox = gtkblist->tipwindow = NULL; + protomenu = NULL; + awaymenu = NULL; + gtkblist = NULL; +} + +static void gaim_gtk_blist_set_visible(struct gaim_buddy_list *list, gboolean show) +{ + if (!(gtkblist && gtkblist->window)) + return; + + if (show) { + gaim_gtk_blist_restore_position(); + gtk_window_present(GTK_WINDOW(gtkblist->window)); + } else { + if (!connections || docklet_count) { +#ifdef _WIN32 + wgaim_systray_minimize(gtkblist->window); +#endif + gtk_widget_hide(gtkblist->window); + } else { + gtk_window_iconify(GTK_WINDOW(gtkblist->window)); + } + } +} + +void gaim_gtk_blist_docklet_toggle() { + /* Useful for the docklet plugin and also for the win32 tray icon*/ + /* This is called when one of those is clicked--it will show/hide the + buddy list/login window--depending on which is active */ + if (connections) { + if (gtkblist && gtkblist->window) { + if (GTK_WIDGET_VISIBLE(gtkblist->window)) { + gaim_blist_set_visible(GAIM_WINDOW_ICONIFIED(gtkblist->window) || gaim_gtk_blist_obscured); + } else { +#if _WIN32 + wgaim_systray_maximize(gtkblist->window); +#endif + gaim_blist_set_visible(TRUE); + } + } else { + /* we're logging in or something... do nothing */ + /* or should I make the blist? */ + gaim_debug(GAIM_DEBUG_WARNING, "blist", + "docklet_toggle called with connections " + "but no blist!\n"); + } + } else if (mainwindow) { + if (GTK_WIDGET_VISIBLE(mainwindow)) { + if (GAIM_WINDOW_ICONIFIED(mainwindow)) { + gtk_window_present(GTK_WINDOW(mainwindow)); + } else { +#if _WIN32 + wgaim_systray_minimize(mainwindow); +#endif + gtk_widget_hide(mainwindow); + } + } else { +#if _WIN32 + wgaim_systray_maximize(mainwindow); +#endif + show_login(); + } + } else { + show_login(); + } +} + +void gaim_gtk_blist_docklet_add() +{ + docklet_count++; +} + +void gaim_gtk_blist_docklet_remove() +{ + docklet_count--; + if (!docklet_count) { + if (connections) + gaim_blist_set_visible(TRUE); + else if (mainwindow) + gtk_window_present(GTK_WINDOW(mainwindow)); + else + show_login(); + } +} + +static struct gaim_blist_ui_ops blist_ui_ops = +{ + gaim_gtk_blist_new_list, + NULL, + gaim_gtk_blist_show, + gaim_gtk_blist_update, + gaim_gtk_blist_remove, + gaim_gtk_blist_destroy, + gaim_gtk_blist_set_visible +}; + + +struct gaim_blist_ui_ops *gaim_get_gtk_blist_ui_ops() +{ + return &blist_ui_ops; +} + + + +/********************************************************************* + * Public utility functions * + *********************************************************************/ + +GdkPixbuf * +create_prpl_icon(struct gaim_account *account) +{ + GaimPlugin *prpl; + GaimPluginProtocolInfo *prpl_info = NULL; + GdkPixbuf *status = NULL; + char *filename = NULL; + const char *protoname = NULL; + char buf[256]; + + prpl = gaim_find_prpl(account->protocol); + + if (prpl != NULL) { + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + + if (prpl_info->list_icon != NULL) + protoname = prpl_info->list_icon(account, NULL); + } + + if (protoname == NULL) + return NULL; + + /* + * Status icons will be themeable too, and then it will look up + * protoname from the theme + */ + g_snprintf(buf, sizeof(buf), "%s.png", protoname); + + filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", + "default", buf, NULL); + status = gdk_pixbuf_new_from_file(filename, NULL); + g_free(filename); + + return status; +} + diff -r 6d1707dc8c3d -r 1a53330dfd34 src/gtkblist.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gtkblist.h Sat Apr 26 17:56:23 2003 +0000 @@ -0,0 +1,128 @@ +/** + * @file gtkblist.h GTK+ Buddy List API + * @ingroup gtkui + * + * gaim + * + * Copyright (C) 2002-2003, Sean Egan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GAIM_GTK_LIST_H_ +#define _GAIM_GTK_LIST_H_ + +enum { + STATUS_ICON_COLUMN, + STATUS_ICON_VISIBLE_COLUMN, + NAME_COLUMN, + WARNING_COLUMN, + IDLE_COLUMN, + BUDDY_ICON_COLUMN, + NODE_COLUMN, + BLIST_COLUMNS + }; + +typedef enum { + GAIM_STATUS_ICON_LARGE, + GAIM_STATUS_ICON_SMALL +} GaimStatusIconSize; +/************************************************************************** + * @name Structures + **************************************************************************/ +/** + * Like, everything you need to know about the gtk buddy list + */ +struct gaim_gtk_buddy_list { + GtkWidget *window; + GtkWidget *vbox; /**< This is the vbox that everything gets packed into. Your plugin might + want to pack something in it itself. Go, plugins! */ + + GtkWidget *treeview; /**< It's a treeview... d'uh. */ + GtkTreeStore *treemodel; /**< This is the treemodel. */ + GtkTreeViewColumn *idle_column, + *warning_column, + *buddy_icon_column; + + GtkWidget *bpmenu; /**< The buddy pounce menu. */ + + GtkWidget *bbox; /**< A Button Box. */ + GtkTooltips *tooltips; /**< Tooltips for the buttons. */ + + guint refresh_timer; /**< The timer for refreshing every 30 seconds */ + + guint timeout; /**< The timeout for the tooltip. */ + GdkRectangle rect; /**< This is the bounding rectangle of the + cell we're currently hovering over. This is + used for tooltips. */ + GtkWidget *tipwindow; /**< The window used by the tooltip */ + + GaimBlistNode *selected_node; /**< The currently selected node */ +}; + +#define GAIM_GTK_BLIST(list) ((struct gaim_gtk_buddy_list *)(list)->ui_data) +#define GAIM_IS_GTK_BLIST(list) \ + ((list)->ui_ops == gaim_get_gtk_blist_ui_ops()) + +/************************************************************************** + * @name GTK+ Conversation API + **************************************************************************/ +/** + * Returns the UI operations structure for the buddy list. + * + * @return The GTK list operations structure. + */ +struct gaim_blist_ui_ops *gaim_get_gtk_blist_ui_ops(void); + +/** + * Returns the base image to represent the account, based on the currently selected theme + * + * @param account The account. + * + * @return The icon + */ +GdkPixbuf *create_prpl_icon(struct gaim_account *account); + +/** + * Refreshes all the nodes of the buddy list. + * This should only be called when something changes to affect most of the nodes (such as a ui preference changing) + * + * @param list This is the core list that gets updated from + */ +void gaim_gtk_blist_refresh(struct gaim_buddy_list *list); + +/** + * Tells the buddy list to update its toolbar based on the preferences + * + */ +void gaim_gtk_blist_update_toolbar(); + +/** + * Useful for the docklet plugin and also for the win32 tray icon + * This is called when one of those is clicked--it will show/hide the + * buddy list/login window--depending on which is active + */ +void gaim_gtk_blist_docklet_toggle(); +void gaim_gtk_blist_docklet_add(); +void gaim_gtk_blist_docklet_remove(); +void gaim_gtk_blist_update_columns(); + +/** + * Useful for the buddy ticker + */ +GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b, + GaimStatusIconSize size); + +#endif /* _GAIM_GTK_LIST_H_ */ diff -r 6d1707dc8c3d -r 1a53330dfd34 src/gtkconv.c --- a/src/gtkconv.c Sat Apr 26 17:36:52 2003 +0000 +++ b/src/gtkconv.c Sat Apr 26 17:56:23 2003 +0000 @@ -43,7 +43,7 @@ #include "gtkimhtml.h" #include "dnd-hints.h" #include "sound.h" -#include "gtklist.h" +#include "gtkblist.h" #ifdef _WIN32 #include "win32dep.h" diff -r 6d1707dc8c3d -r 1a53330dfd34 src/gtklist.h --- a/src/gtklist.h Sat Apr 26 17:36:52 2003 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,128 +0,0 @@ -/** - * @file gtklist.h GTK+ Buddy List API - * @ingroup gtkui - * - * gaim - * - * Copyright (C) 2002-2003, Sean Egan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _GAIM_GTK_LIST_H_ -#define _GAIM_GTK_LIST_H_ - -enum { - STATUS_ICON_COLUMN, - STATUS_ICON_VISIBLE_COLUMN, - NAME_COLUMN, - WARNING_COLUMN, - IDLE_COLUMN, - BUDDY_ICON_COLUMN, - NODE_COLUMN, - BLIST_COLUMNS - }; - -typedef enum { - GAIM_STATUS_ICON_LARGE, - GAIM_STATUS_ICON_SMALL -} GaimStatusIconSize; -/************************************************************************** - * @name Structures - **************************************************************************/ -/** - * Like, everything you need to know about the gtk buddy list - */ -struct gaim_gtk_buddy_list { - GtkWidget *window; - GtkWidget *vbox; /**< This is the vbox that everything gets packed into. Your plugin might - want to pack something in it itself. Go, plugins! */ - - GtkWidget *treeview; /**< It's a treeview... d'uh. */ - GtkTreeStore *treemodel; /**< This is the treemodel. */ - GtkTreeViewColumn *idle_column, - *warning_column, - *buddy_icon_column; - - GtkWidget *bpmenu; /**< The buddy pounce menu. */ - - GtkWidget *bbox; /**< A Button Box. */ - GtkTooltips *tooltips; /**< Tooltips for the buttons. */ - - guint refresh_timer; /**< The timer for refreshing every 30 seconds */ - - guint timeout; /**< The timeout for the tooltip. */ - GdkRectangle rect; /**< This is the bounding rectangle of the - cell we're currently hovering over. This is - used for tooltips. */ - GtkWidget *tipwindow; /**< The window used by the tooltip */ - - GaimBlistNode *selected_node; /**< The currently selected node */ -}; - -#define GAIM_GTK_BLIST(list) ((struct gaim_gtk_buddy_list *)(list)->ui_data) -#define GAIM_IS_GTK_BLIST(list) \ - ((list)->ui_ops == gaim_get_gtk_blist_ui_ops()) - -/************************************************************************** - * @name GTK+ Conversation API - **************************************************************************/ -/** - * Returns the UI operations structure for the buddy list. - * - * @return The GTK list operations structure. - */ -struct gaim_blist_ui_ops *gaim_get_gtk_blist_ui_ops(void); - -/** - * Returns the base image to represent the account, based on the currently selected theme - * - * @param account The account. - * - * @return The icon - */ -GdkPixbuf *create_prpl_icon(struct gaim_account *account); - -/** - * Refreshes all the nodes of the buddy list. - * This should only be called when something changes to affect most of the nodes (such as a ui preference changing) - * - * @param list This is the core list that gets updated from - */ -void gaim_gtk_blist_refresh(struct gaim_buddy_list *list); - -/** - * Tells the buddy list to update its toolbar based on the preferences - * - */ -void gaim_gtk_blist_update_toolbar(); - -/** - * Useful for the docklet plugin and also for the win32 tray icon - * This is called when one of those is clicked--it will show/hide the - * buddy list/login window--depending on which is active - */ -void gaim_gtk_blist_docklet_toggle(); -void gaim_gtk_blist_docklet_add(); -void gaim_gtk_blist_docklet_remove(); -void gaim_gtk_blist_update_columns(); - -/** - * Useful for the buddy ticker - */ -GdkPixbuf *gaim_gtk_blist_get_status_icon(struct buddy *b, - GaimStatusIconSize size); - -#endif /* _GAIM_GTK_LIST_H_ */ diff -r 6d1707dc8c3d -r 1a53330dfd34 src/gtkpounce.c --- a/src/gtkpounce.c Sat Apr 26 17:36:52 2003 +0000 +++ b/src/gtkpounce.c Sat Apr 26 17:56:23 2003 +0000 @@ -23,7 +23,7 @@ #include #include "gaim.h" #include "gtkpounce.h" -#include "gtklist.h" +#include "gtkblist.h" #include "prpl.h" #include "sound.h" diff -r 6d1707dc8c3d -r 1a53330dfd34 src/list.c --- a/src/list.c Sat Apr 26 17:36:52 2003 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1546 +0,0 @@ -/* - * gaim - * - * Copyright (C) 2003, Sean Egan - * Copyright (C) 1998-1999, Mark Spencer - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif -#include -#include -#include -#include -#ifndef _WIN32 -#include -#else -#include -#endif -#include -#include "gaim.h" -#include "prpl.h" -#include "list.h" - -#ifdef _WIN32 -#include "win32dep.h" -#endif - -#define PATHSIZE 1024 - -struct gaim_buddy_list *gaimbuddylist = NULL; -static struct gaim_blist_ui_ops *blist_ui_ops = NULL; - -/***************************************************************************** - * Private Utility functions * - *****************************************************************************/ -static GaimBlistNode *gaim_blist_get_last_sibling(GaimBlistNode *node) -{ - GaimBlistNode *n = node; - if (!n) - return NULL; - while (n->next) - n = n->next; - return n; -} -static GaimBlistNode *gaim_blist_get_last_child(GaimBlistNode *node) -{ - if (!node) - return NULL; - return gaim_blist_get_last_sibling(node->child); -} - -/***************************************************************************** - * Public API functions * - *****************************************************************************/ - -struct gaim_buddy_list *gaim_blist_new() -{ - struct gaim_buddy_list *gbl = g_new0(struct gaim_buddy_list, 1); - - gbl->ui_ops = gaim_get_blist_ui_ops(); - - if (gbl->ui_ops != NULL && gbl->ui_ops->new_list != NULL) - gbl->ui_ops->new_list(gbl); - - return gbl; -} - -void -gaim_set_blist(struct gaim_buddy_list *list) -{ - gaimbuddylist = list; -} - -struct gaim_buddy_list * -gaim_get_blist(void) -{ - return gaimbuddylist; -} - -void gaim_blist_show () -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - if (ops) - ops->show(gaimbuddylist); -} - -void gaim_blist_destroy() -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - if (ops) - ops->destroy(gaimbuddylist); -} - -void gaim_blist_set_visible (gboolean show) -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - if (ops) - ops->set_visible(gaimbuddylist, show); -} - -void gaim_blist_update_buddy_status (struct buddy *buddy, int status) -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - buddy->uc = status; - - if(!(status & UC_UNAVAILABLE)) - gaim_event_broadcast(event_buddy_back, buddy->account->gc, buddy->name); - else - gaim_event_broadcast(event_buddy_away, buddy->account->gc, buddy->name); - - if (ops) - ops->update(gaimbuddylist, (GaimBlistNode*)buddy); -} - -static gboolean presence_update_timeout_cb(struct buddy *buddy) { - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - - if(buddy->present == GAIM_BUDDY_SIGNING_ON) { - buddy->present = GAIM_BUDDY_ONLINE; - gaim_event_broadcast(event_buddy_signon, buddy->account->gc, buddy->name); - } else if(buddy->present == GAIM_BUDDY_SIGNING_OFF) { - buddy->present = GAIM_BUDDY_OFFLINE; - } - - buddy->timer = 0; - - if (ops) - ops->update(gaimbuddylist, (GaimBlistNode*)buddy); - - return FALSE; -} - -void gaim_blist_update_buddy_presence(struct buddy *buddy, int presence) { - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - gboolean do_timer = FALSE; - - if (!GAIM_BUDDY_IS_ONLINE(buddy) && presence) { - buddy->present = GAIM_BUDDY_SIGNING_ON; - gaim_event_broadcast(event_buddy_signon, buddy->account->gc, buddy->name); - do_timer = TRUE; - } else if(GAIM_BUDDY_IS_ONLINE(buddy) && !presence) { - buddy->present = GAIM_BUDDY_SIGNING_OFF; - gaim_event_broadcast(event_buddy_signoff, buddy->account->gc, buddy->name); - do_timer = TRUE; - } - - if(do_timer) { - if(buddy->timer > 0) - g_source_remove(buddy->timer); - buddy->timer = g_timeout_add(10000, (GSourceFunc)presence_update_timeout_cb, buddy); - } - - if (ops) - ops->update(gaimbuddylist, (GaimBlistNode*)buddy); -} - - -void gaim_blist_update_buddy_idle (struct buddy *buddy, int idle) -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - buddy->idle = idle; - if (ops) - ops->update(gaimbuddylist, (GaimBlistNode*)buddy); -} -void gaim_blist_update_buddy_evil (struct buddy *buddy, int warning) -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - buddy->evil = warning; - if (ops) - ops->update(gaimbuddylist,(GaimBlistNode*)buddy); -} -void gaim_blist_update_buddy_icon(struct buddy *buddy) { - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - if(ops) - ops->update(gaimbuddylist, (GaimBlistNode*)buddy); -} -void gaim_blist_rename_buddy (struct buddy *buddy, const char *name) -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - g_free(buddy->name); - buddy->name = g_strdup(name); - if (ops) - ops->update(gaimbuddylist, (GaimBlistNode*)buddy); -} -void gaim_blist_alias_buddy (struct buddy *buddy, const char *alias) -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - struct gaim_conversation *conv; - - g_free(buddy->alias); - - if(alias && strlen(alias)) - buddy->alias = g_strdup(alias); - else - buddy->alias = NULL; - - if (ops) - ops->update(gaimbuddylist, (GaimBlistNode*)buddy); - - conv = gaim_find_conversation_with_account(buddy->name, buddy->account); - - if (conv) - gaim_conversation_autoset_title(conv); -} - -void gaim_blist_rename_group(struct group *group, const char *name) -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - g_free(group->name); - group->name = g_strdup(name); - if (ops) - ops->update(gaimbuddylist, (GaimBlistNode*)group); -} -struct buddy *gaim_buddy_new(struct gaim_account *account, const char *screenname, const char *alias) -{ - struct buddy *b; - struct gaim_blist_ui_ops *ops; - - b = g_new0(struct buddy, 1); - b->account = account; - b->name = g_strdup(screenname); - b->alias = g_strdup(alias); - b->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - ((GaimBlistNode*)b)->type = GAIM_BLIST_BUDDY_NODE; - - ops = gaim_get_blist_ui_ops(); - - if (ops != NULL && ops->new_node != NULL) - ops->new_node((GaimBlistNode *)b); - - return b; -} -void gaim_blist_add_buddy (struct buddy *buddy, struct group *group, GaimBlistNode *node) -{ - GaimBlistNode *n = node, *bnode = (GaimBlistNode*)buddy; - struct group *g = group; - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - gboolean save = FALSE; - - if (!n) { - if (!g) { - g = gaim_group_new(_("Buddies")); - gaim_blist_add_group(g, NULL); - } - n = gaim_blist_get_last_child((GaimBlistNode*)g); - } else { - g = (struct group*)n->parent; - } - - /* if we're moving to overtop of ourselves, do nothing */ - if(bnode == n) - return; - - if (bnode->parent) { - /* This buddy was already in the list and is - * being moved. - */ - if(bnode->next) - bnode->next->prev = bnode->prev; - if(bnode->prev) - bnode->prev->next = bnode->next; - if(bnode->parent->child == bnode) - bnode->parent->child = bnode->next; - - ops->remove(gaimbuddylist, bnode); - - if (bnode->parent != ((GaimBlistNode*)g)) - serv_move_buddy(buddy, (struct group*)bnode->parent, g); - save = TRUE; - } - - if (n) { - if(n->next) - n->next->prev = (GaimBlistNode*)buddy; - ((GaimBlistNode*)buddy)->next = n->next; - ((GaimBlistNode*)buddy)->prev = n; - ((GaimBlistNode*)buddy)->parent = n->parent; - n->next = (GaimBlistNode*)buddy; - } else { - ((GaimBlistNode*)g)->child = (GaimBlistNode*)buddy; - ((GaimBlistNode*)buddy)->next = NULL; - ((GaimBlistNode*)buddy)->prev = NULL; - ((GaimBlistNode*)buddy)->parent = (GaimBlistNode*)g; - } - - if (ops) - ops->update(gaimbuddylist, (GaimBlistNode*)buddy); - if (save) - gaim_blist_save(); -} - -struct group *gaim_group_new(const char *name) -{ - struct group *g = gaim_find_group(name); - - if (!g) { - struct gaim_blist_ui_ops *ops; - g= g_new0(struct group, 1); - g->name = g_strdup(name); - g->settings = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, g_free); - ((GaimBlistNode*)g)->type = GAIM_BLIST_GROUP_NODE; - - ops = gaim_get_blist_ui_ops(); - - if (ops != NULL && ops->new_node != NULL) - ops->new_node((GaimBlistNode *)g); - - } - return g; -} - -void gaim_blist_add_group (struct group *group, GaimBlistNode *node) -{ - struct gaim_blist_ui_ops *ops; - GaimBlistNode *gnode = (GaimBlistNode*)group; - gboolean save = FALSE; - - if (!gaimbuddylist) - gaimbuddylist = gaim_blist_new(); - ops = gaimbuddylist->ui_ops; - - if (!gaimbuddylist->root) { - gaimbuddylist->root = gnode; - return; - } - - if (!node) - node = gaim_blist_get_last_sibling(gaimbuddylist->root); - - /* if we're moving to overtop of ourselves, do nothing */ - if(gnode == node) - return; - - if (gaim_find_group(group->name)) { - /* This is just being moved */ - - ops->remove(gaimbuddylist, (GaimBlistNode*)group); - - if(gnode == gaimbuddylist->root) - gaimbuddylist->root = gnode->next; - if(gnode->prev) - gnode->prev->next = gnode->next; - if(gnode->next) - gnode->next->prev = gnode->prev; - - save = TRUE; - } - - gnode->next = node->next; - gnode->prev = node; - if(node->next) - node->next->prev = gnode; - node->next = gnode; - - if (ops) { - ops->update(gaimbuddylist, gnode); - for(node = gnode->child; node; node = node->next) - ops->update(gaimbuddylist, node); - } - if (save) - gaim_blist_save(); -} - -void gaim_blist_remove_buddy (struct buddy *buddy) -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - - GaimBlistNode *gnode, *node = (GaimBlistNode*)buddy; - struct group *group; - - gnode = node->parent; - group = (struct group *)gnode; - - if(gnode->child == node) - gnode->child = node->next; - if (node->prev) - node->prev->next = node->next; - if (node->next) - node->next->prev = node->prev; - - ops->remove(gaimbuddylist, node); - g_hash_table_destroy(buddy->settings); - g_free(buddy->name); - g_free(buddy->alias); - g_free(buddy); -} - -void gaim_blist_remove_group (struct group *group) -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - GaimBlistNode *node = (GaimBlistNode*)group; - - if(node->child) { - char *buf; - int count = 0; - GaimBlistNode *child = node->child; - - while(child) { - count++; - child = child->next; - } - - buf = g_strdup_printf(_("%d buddies from group %s were not " - "removed because their accounts were not logged in. These " - "buddies, and the group were not removed.\n"), - count, group->name); - do_error_dialog(_("Group Not Removed"), buf, GAIM_ERROR); - g_free(buf); - return; - } - - if(gaimbuddylist->root == node) - gaimbuddylist->root = node->next; - if (node->prev) - node->prev->next = node->next; - if (node->next) - node->next->prev = node->prev; - - ops->remove(gaimbuddylist, node); - g_free(group->name); - g_free(group); -} - -char *gaim_get_buddy_alias_only(struct buddy *b) { - if(!b) - return NULL; - if(b->alias && b->alias[0]) - return b->alias; - else if((misc_options & OPT_MISC_USE_SERVER_ALIAS) && b->server_alias) - return b->server_alias; - return NULL; -} - -char * gaim_get_buddy_alias (struct buddy *buddy) -{ - char *ret = gaim_get_buddy_alias_only(buddy); - if(!ret) - return buddy ? buddy->name : _("Unknown"); - return ret; - -} - -struct buddy *gaim_find_buddy(struct gaim_account *account, const char *name) -{ - GaimBlistNode *group; - GaimBlistNode *buddy; - char *norm_name = g_strdup(normalize(name)); - - if (!gaimbuddylist) - return NULL; - - group = gaimbuddylist->root; - while (group) { - buddy = group->child; - while (buddy) { - if (!gaim_utf8_strcasecmp(normalize(((struct buddy*)buddy)->name), norm_name) && account == ((struct buddy*)buddy)->account) { - g_free(norm_name); - return (struct buddy*)buddy; - } - buddy = buddy->next; - } - group = group->next; - } - g_free(norm_name); - return NULL; -} - -struct group *gaim_find_group(const char *name) -{ - GaimBlistNode *node; - if (!gaimbuddylist) - return NULL; - node = gaimbuddylist->root; - while(node) { - if (!strcmp(((struct group*)node)->name, name)) - return (struct group*)node; - node = node->next; - } - return NULL; -} -struct group *gaim_find_buddys_group(struct buddy *buddy) -{ - if (!buddy) - return NULL; - return (struct group*)(((GaimBlistNode*)buddy)->parent); -} - -GSList *gaim_group_get_accounts(struct group *g) -{ - GSList *l = NULL; - GaimBlistNode *child = ((GaimBlistNode *)g)->child; - - while (child) { - if (!g_slist_find(l, ((struct buddy*)child)->account)) - l = g_slist_append(l, ((struct buddy*)child)->account); - child = child->next; - } - return l; -} - -void gaim_blist_remove_account(struct gaim_account *account) -{ - struct gaim_blist_ui_ops *ops = gaimbuddylist->ui_ops; - GaimBlistNode *group = gaimbuddylist->root; - GaimBlistNode *buddy; - if (!gaimbuddylist) - return; - while (group) { - buddy = group->child; - while (buddy) { - if (account == ((struct buddy*)buddy)->account) { - ((struct buddy*)buddy)->present = GAIM_BUDDY_OFFLINE; - if(ops) - ops->remove(gaimbuddylist, buddy); - } - buddy = buddy->next; - } - group = group->next; - } -} - -void parse_toc_buddy_list(struct gaim_account *account, char *config) -{ - char *c; - char current[256]; - GList *bud = NULL; - - - if (config != NULL) { - - /* skip "CONFIG:" (if it exists) */ - c = strncmp(config + 6 /* sizeof(struct sflap_hdr) */ , "CONFIG:", strlen("CONFIG:")) ? - strtok(config, "\n") : - strtok(config + 6 /* sizeof(struct sflap_hdr) */ + strlen("CONFIG:"), "\n"); - do { - if (c == NULL) - break; - if (*c == 'g') { - char *utf8 = NULL; - utf8 = gaim_try_conv_to_utf8(c + 2); - if (utf8 == NULL) { - g_strlcpy(current, _("Invalid Groupname"), sizeof(current)); - } else { - g_strlcpy(current, utf8, sizeof(current)); - g_free(utf8); - } - if (!gaim_find_group(current)) { - struct group *g = gaim_group_new(current); - gaim_blist_add_group(g, NULL); - } - } else if (*c == 'b') { /*&& !gaim_find_buddy(user, c + 2)) {*/ - char nm[80], sw[388], *a, *utf8 = NULL; - - if ((a = strchr(c + 2, ':')) != NULL) { - *a++ = '\0'; /* nul the : */ - } - - g_strlcpy(nm, c + 2, sizeof(nm)); - if (a) { - utf8 = gaim_try_conv_to_utf8(a); - if (utf8 == NULL) { - gaim_debug(GAIM_DEBUG_ERROR, "toc blist", - "Failed to convert alias for " - "'%s' to UTF-8\n", nm); - } - } - if (utf8 == NULL) { - sw[0] = '\0'; - } else { - /* This can leave a partial sequence at the end, - * but who cares? */ - g_strlcpy(sw, utf8, sizeof(sw)); - g_free(utf8); - } - - if (!gaim_find_buddy(account, nm)) { - struct buddy *b = gaim_buddy_new(account, nm, sw); - struct group *g = gaim_find_group(current); - gaim_blist_add_buddy(b, g, NULL); - bud = g_list_append(bud, g_strdup(nm)); - } - } else if (*c == 'p') { - gaim_privacy_permit_add(account, c + 2); - } else if (*c == 'd') { - gaim_privacy_deny_add(account, c + 2); - } else if (!strncmp("toc", c, 3)) { - sscanf(c + strlen(c) - 1, "%d", &account->permdeny); - gaim_debug(GAIM_DEBUG_MISC, "toc blist", - "permdeny: %d\n", account->permdeny); - if (account->permdeny == 0) - account->permdeny = 1; - } else if (*c == 'm') { - sscanf(c + 2, "%d", &account->permdeny); - gaim_debug(GAIM_DEBUG_MISC, "toc blist", - "permdeny: %d\n", account->permdeny); - if (account->permdeny == 0) - account->permdeny = 1; - } - } while ((c = strtok(NULL, "\n"))); - - if(account->gc) { - if(bud) { - GList *node = bud; - serv_add_buddies(account->gc, bud); - while(node) { - g_free(node->data); - node = node->next; - } - } - serv_set_permit_deny(account->gc); - } - g_list_free(bud); - } -} - -#if 0 -/* translate an AIM 3 buddylist (*.lst) to a Gaim buddylist */ -static GString *translate_lst(FILE *src_fp) -{ - char line[BUF_LEN], *line2; - char *name; - int i; - - GString *dest = g_string_new("m 1\n"); - - while (fgets(line, BUF_LEN, src_fp)) { - line2 = g_strchug(line); - if (strstr(line2, "group") == line2) { - name = strpbrk(line2, " \t\n\r\f") + 1; - dest = g_string_append(dest, "g "); - for (i = 0; i < strcspn(name, "\n\r"); i++) - if (name[i] != '\"') - dest = g_string_append_c(dest, name[i]); - dest = g_string_append_c(dest, '\n'); - } - if (strstr(line2, "buddy") == line2) { - name = strpbrk(line2, " \t\n\r\f") + 1; - dest = g_string_append(dest, "b "); - for (i = 0; i < strcspn(name, "\n\r"); i++) - if (name[i] != '\"') - dest = g_string_append_c(dest, name[i]); - dest = g_string_append_c(dest, '\n'); - } - } - - return dest; -} - - -/* translate an AIM 4 buddylist (*.blt) to Gaim format */ -static GString *translate_blt(FILE *src_fp) -{ - int i; - char line[BUF_LEN]; - char *buddy; - - GString *dest = g_string_new("m 1\n"); - - while (strstr(fgets(line, BUF_LEN, src_fp), "Buddy") == NULL); - while (strstr(fgets(line, BUF_LEN, src_fp), "list") == NULL); - - while (1) { - fgets(line, BUF_LEN, src_fp); g_strchomp(line); - if (strchr(line, '}') != NULL) - break; - - if (strchr(line, '{') != NULL) { - /* Syntax starting with " {" */ - - dest = g_string_append(dest, "g "); - buddy = g_strchug(strtok(line, "{")); - for (i = 0; i < strlen(buddy); i++) - if (buddy[i] != '\"') - dest = g_string_append_c(dest, buddy[i]); - dest = g_string_append_c(dest, '\n'); - while (strchr(fgets(line, BUF_LEN, src_fp), '}') == NULL) { - gboolean pounce = FALSE; - char *e; - g_strchomp(line); - buddy = g_strchug(line); - gaim_debug(GAIM_DEBUG_MISC, "AIM 4 blt import", - "buddy: \"%s\"\n", buddy); - dest = g_string_append(dest, "b "); - if (strchr(buddy, '{') != NULL) { - /* buddy pounce, etc */ - char *pos = strchr(buddy, '{') - 1; - *pos = 0; - pounce = TRUE; - } - if ((e = strchr(buddy, '\"')) != NULL) { - *e = '\0'; - buddy++; - } - dest = g_string_append(dest, buddy); - dest = g_string_append_c(dest, '\n'); - if (pounce) - do - fgets(line, BUF_LEN, src_fp); - while (!strchr(line, '}')); - } - } else { - - /* Syntax "group buddy buddy ..." */ - buddy = g_strchug(strtok(line, " \n")); - dest = g_string_append(dest, "g "); - if (strchr(buddy, '\"') != NULL) { - dest = g_string_append(dest, &buddy[1]); - dest = g_string_append_c(dest, ' '); - buddy = g_strchug(strtok(NULL, " \n")); - while (strchr(buddy, '\"') == NULL) { - dest = g_string_append(dest, buddy); - dest = g_string_append_c(dest, ' '); - buddy = g_strchug(strtok(NULL, " \n")); - } - buddy[strlen(buddy) - 1] = '\0'; - dest = g_string_append(dest, buddy); - } else { - dest = g_string_append(dest, buddy); - } - dest = g_string_append_c(dest, '\n'); - while ((buddy = g_strchug(strtok(NULL, " \n"))) != NULL) { - dest = g_string_append(dest, "b "); - if (strchr(buddy, '\"') != NULL) { - dest = g_string_append(dest, &buddy[1]); - dest = g_string_append_c(dest, ' '); - buddy = g_strchug(strtok(NULL, " \n")); - while (strchr(buddy, '\"') == NULL) { - dest = g_string_append(dest, buddy); - dest = g_string_append_c(dest, ' '); - buddy = g_strchug(strtok(NULL, " \n")); - } - buddy[strlen(buddy) - 1] = '\0'; - dest = g_string_append(dest, buddy); - } else { - dest = g_string_append(dest, buddy); - } - dest = g_string_append_c(dest, '\n'); - } - } - } - - return dest; -} - -static GString *translate_gnomeicu(FILE *src_fp) -{ - char line[BUF_LEN]; - GString *dest = g_string_new("m 1\ng Buddies\n"); - - while (strstr(fgets(line, BUF_LEN, src_fp), "NewContacts") == NULL); - - while (fgets(line, BUF_LEN, src_fp)) { - char *eq; - g_strchomp(line); - if (line[0] == '\n' || line[0] == '[') - break; - eq = strchr(line, '='); - if (!eq) - break; - *eq = ':'; - eq = strchr(eq, ','); - if (eq) - *eq = '\0'; - dest = g_string_append(dest, "b "); - dest = g_string_append(dest, line); - dest = g_string_append_c(dest, '\n'); - } - - return dest; -} -#endif - -static gchar *get_screenname_filename(const char *name) -{ - gchar **split; - gchar *good; - gchar *ret; - - split = g_strsplit(name, G_DIR_SEPARATOR_S, -1); - good = g_strjoinv(NULL, split); - g_strfreev(split); - - ret = g_utf8_strup(good, -1); - - g_free(good); - - return ret; -} - -static gboolean gaim_blist_read(const char *filename); - - -static void do_import(struct gaim_account *account, const char *filename) -{ - GString *buf = NULL; - char first[64]; - char path[PATHSIZE]; - int len; - FILE *f; - struct stat st; - - if (filename) { - g_snprintf(path, sizeof(path), "%s", filename); - } else { - char *g_screenname = get_screenname_filename(account->username); - char *file = gaim_user_dir(); - int protocol = (account->protocol == GAIM_PROTO_OSCAR) ? (isalpha(account->username[0]) ? GAIM_PROTO_TOC : GAIM_PROTO_ICQ): account->protocol; - - if (file != (char *)NULL) { - sprintf(path, "%s" G_DIR_SEPARATOR_S "%s.%d.blist", file, g_screenname, protocol); - g_free(g_screenname); - } else { - g_free(g_screenname); - return; - } - } - - if (stat(path, &st)) { - gaim_debug(GAIM_DEBUG_ERROR, "blist import", "Unable to stat %s.\n", - path); - return; - } - - if (!(f = fopen(path, "r"))) { - gaim_debug(GAIM_DEBUG_ERROR, "blist import", "Unable to open %s.\n", - path); - return; - } - - fgets(first, 64, f); - - if ((first[0] == '\n') || (first[0] == '\r' && first[1] == '\n')) - fgets(first, 64, f); - -#if 0 - if (!g_strncasecmp(first, "str); - g_string_free(buf, TRUE); - } -} - -gboolean gaim_group_on_account(struct group *g, struct gaim_account *account) { - GaimBlistNode *bnode; - for(bnode = g->node.child; bnode; bnode = bnode->next) { - struct buddy *b = (struct buddy *)bnode; - if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) - continue; - if((!account && b->account->gc) || b->account == account) - return TRUE; - } - return FALSE; -} - -static gboolean blist_safe_to_write = FALSE; - -static char *blist_parser_group_name = NULL; -static char *blist_parser_person_name = NULL; -static char *blist_parser_account_name = NULL; -static int blist_parser_account_protocol = 0; -static char *blist_parser_buddy_name = NULL; -static char *blist_parser_buddy_alias = NULL; -static char *blist_parser_setting_name = NULL; -static char *blist_parser_setting_value = NULL; -static GHashTable *blist_parser_buddy_settings = NULL; -static GHashTable *blist_parser_group_settings = NULL; -static int blist_parser_privacy_mode = 0; -static GList *tag_stack = NULL; -enum { - BLIST_TAG_GAIM, - BLIST_TAG_BLIST, - BLIST_TAG_GROUP, - BLIST_TAG_PERSON, - BLIST_TAG_BUDDY, - BLIST_TAG_NAME, - BLIST_TAG_ALIAS, - BLIST_TAG_SETTING, - BLIST_TAG_PRIVACY, - BLIST_TAG_ACCOUNT, - BLIST_TAG_PERMIT, - BLIST_TAG_BLOCK, - BLIST_TAG_IGNORE -}; -static gboolean blist_parser_error_occurred = FALSE; - -static void blist_start_element_handler (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attribute_names, - const gchar **attribute_values, - gpointer user_data, - GError **error) { - int i; - - if(!strcmp(element_name, "gaim")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_GAIM)); - } else if(!strcmp(element_name, "blist")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BLIST)); - } else if(!strcmp(element_name, "group")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_GROUP)); - for(i=0; attribute_names[i]; i++) { - if(!strcmp(attribute_names[i], "name")) { - g_free(blist_parser_group_name); - blist_parser_group_name = g_strdup(attribute_values[i]); - } - } - if(blist_parser_group_name) { - struct group *g = gaim_group_new(blist_parser_group_name); - gaim_blist_add_group(g,NULL); - } - } else if(!strcmp(element_name, "person")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PERSON)); - for(i=0; attribute_names[i]; i++) { - if(!strcmp(attribute_names[i], "name")) { - g_free(blist_parser_person_name); - blist_parser_person_name = g_strdup(attribute_values[i]); - } - } - } else if(!strcmp(element_name, "buddy")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BUDDY)); - for(i=0; attribute_names[i]; i++) { - if(!strcmp(attribute_names[i], "account")) { - g_free(blist_parser_account_name); - blist_parser_account_name = g_strdup(attribute_values[i]); - } else if(!strcmp(attribute_names[i], "protocol")) { - blist_parser_account_protocol = atoi(attribute_values[i]); - } - } - } else if(!strcmp(element_name, "name")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_NAME)); - } else if(!strcmp(element_name, "alias")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_ALIAS)); - } else if(!strcmp(element_name, "setting")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_SETTING)); - for(i=0; attribute_names[i]; i++) { - if(!strcmp(attribute_names[i], "name")) { - g_free(blist_parser_setting_name); - blist_parser_setting_name = g_strdup(attribute_values[i]); - } - } - } else if(!strcmp(element_name, "privacy")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PRIVACY)); - } else if(!strcmp(element_name, "account")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_ACCOUNT)); - for(i=0; attribute_names[i]; i++) { - if(!strcmp(attribute_names[i], "protocol")) - blist_parser_account_protocol = atoi(attribute_values[i]); - else if(!strcmp(attribute_names[i], "mode")) - blist_parser_privacy_mode = atoi(attribute_values[i]); - else if(!strcmp(attribute_names[i], "name")) { - g_free(blist_parser_account_name); - blist_parser_account_name = g_strdup(attribute_values[i]); - } - } - } else if(!strcmp(element_name, "permit")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_PERMIT)); - } else if(!strcmp(element_name, "block")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_BLOCK)); - } else if(!strcmp(element_name, "ignore")) { - tag_stack = g_list_prepend(tag_stack, GINT_TO_POINTER(BLIST_TAG_IGNORE)); - } -} - -static void blist_end_element_handler(GMarkupParseContext *context, - const gchar *element_name, gpointer user_data, GError **error) { - if(!strcmp(element_name, "gaim")) { - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } else if(!strcmp(element_name, "blist")) { - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } else if(!strcmp(element_name, "group")) { - if(blist_parser_group_settings) { - struct group *g = gaim_find_group(blist_parser_group_name); - g_hash_table_destroy(g->settings); - g->settings = blist_parser_group_settings; - } - tag_stack = g_list_delete_link(tag_stack, tag_stack); - blist_parser_group_settings = NULL; - } else if(!strcmp(element_name, "person")) { - g_free(blist_parser_person_name); - blist_parser_person_name = NULL; - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } else if(!strcmp(element_name, "buddy")) { - struct gaim_account *account = gaim_account_find(blist_parser_account_name, - blist_parser_account_protocol); - if(account) { - struct buddy *b = gaim_buddy_new(account, blist_parser_buddy_name, blist_parser_buddy_alias); - struct group *g = gaim_find_group(blist_parser_group_name); - gaim_blist_add_buddy(b,g,NULL); - if(blist_parser_buddy_settings) { - g_hash_table_destroy(b->settings); - b->settings = blist_parser_buddy_settings; - } - } - g_free(blist_parser_buddy_name); - blist_parser_buddy_name = NULL; - g_free(blist_parser_buddy_alias); - blist_parser_buddy_alias = NULL; - g_free(blist_parser_account_name); - blist_parser_account_name = NULL; - blist_parser_buddy_settings = NULL; - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } else if(!strcmp(element_name, "name")) { - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } else if(!strcmp(element_name, "alias")) { - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } else if(!strcmp(element_name, "setting")) { - if(GPOINTER_TO_INT(tag_stack->next->data) == BLIST_TAG_BUDDY) { - if(!blist_parser_buddy_settings) - blist_parser_buddy_settings = g_hash_table_new_full(g_str_hash, - g_str_equal, g_free, g_free); - if(blist_parser_setting_name && blist_parser_setting_value) { - g_hash_table_replace(blist_parser_buddy_settings, - g_strdup(blist_parser_setting_name), - g_strdup(blist_parser_setting_value)); - } - } else if(GPOINTER_TO_INT(tag_stack->next->data) == BLIST_TAG_GROUP) { - if(!blist_parser_group_settings) - blist_parser_group_settings = g_hash_table_new_full(g_str_hash, - g_str_equal, g_free, g_free); - if(blist_parser_setting_name && blist_parser_setting_value) { - g_hash_table_replace(blist_parser_group_settings, - g_strdup(blist_parser_setting_name), - g_strdup(blist_parser_setting_value)); - } - } - g_free(blist_parser_setting_name); - g_free(blist_parser_setting_value); - blist_parser_setting_name = NULL; - blist_parser_setting_value = NULL; - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } else if(!strcmp(element_name, "privacy")) { - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } else if(!strcmp(element_name, "account")) { - struct gaim_account *account = gaim_account_find(blist_parser_account_name, - blist_parser_account_protocol); - if(account) { - account->permdeny = blist_parser_privacy_mode; - } - g_free(blist_parser_account_name); - blist_parser_account_name = NULL; - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } else if(!strcmp(element_name, "permit")) { - struct gaim_account *account = gaim_account_find(blist_parser_account_name, - blist_parser_account_protocol); - if(account) { - gaim_privacy_permit_add(account, blist_parser_buddy_name); - } - g_free(blist_parser_buddy_name); - blist_parser_buddy_name = NULL; - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } else if(!strcmp(element_name, "block")) { - struct gaim_account *account = gaim_account_find(blist_parser_account_name, - blist_parser_account_protocol); - if(account) { - gaim_privacy_deny_add(account, blist_parser_buddy_name); - } - g_free(blist_parser_buddy_name); - blist_parser_buddy_name = NULL; - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } else if(!strcmp(element_name, "ignore")) { - /* we'll apparently do something with this later */ - tag_stack = g_list_delete_link(tag_stack, tag_stack); - } -} - -static void blist_text_handler(GMarkupParseContext *context, const gchar *text, - gsize text_len, gpointer user_data, GError **error) { - switch(GPOINTER_TO_INT(tag_stack->data)) { - case BLIST_TAG_NAME: - blist_parser_buddy_name = g_strndup(text, text_len); - break; - case BLIST_TAG_ALIAS: - blist_parser_buddy_alias = g_strndup(text, text_len); - break; - case BLIST_TAG_PERMIT: - case BLIST_TAG_BLOCK: - case BLIST_TAG_IGNORE: - blist_parser_buddy_name = g_strndup(text, text_len); - break; - case BLIST_TAG_SETTING: - blist_parser_setting_value = g_strndup(text, text_len); - break; - default: - break; - } -} - -static void blist_error_handler(GMarkupParseContext *context, GError *error, - gpointer user_data) { - blist_parser_error_occurred = TRUE; - gaim_debug(GAIM_DEBUG_ERROR, "blist import", - "Error parsing blist.xml: %s\n", error->message); -} - -static GMarkupParser blist_parser = { - blist_start_element_handler, - blist_end_element_handler, - blist_text_handler, - NULL, - blist_error_handler -}; - -static gboolean gaim_blist_read(const char *filename) { - gchar *contents = NULL; - gsize length; - GMarkupParseContext *context; - GError *error = NULL; - - gaim_debug(GAIM_DEBUG_INFO, "blist import", - "Reading %s\n", filename); - if(!g_file_get_contents(filename, &contents, &length, &error)) { - gaim_debug(GAIM_DEBUG_ERROR, "blist import", - "Error reading blist: %s\n", error->message); - g_error_free(error); - return FALSE; - } - - context = g_markup_parse_context_new(&blist_parser, 0, NULL, NULL); - - if(!g_markup_parse_context_parse(context, contents, length, NULL)) { - g_markup_parse_context_free(context); - g_free(contents); - return FALSE; - } - - if(!g_markup_parse_context_end_parse(context, NULL)) { - gaim_debug(GAIM_DEBUG_ERROR, "blist import", - "Error parsing %s\n", filename); - g_markup_parse_context_free(context); - g_free(contents); - return FALSE; - } - - g_markup_parse_context_free(context); - g_free(contents); - - if(blist_parser_error_occurred) - return FALSE; - - gaim_debug(GAIM_DEBUG_INFO, "blist import", "Finished reading %s\n", - filename); - - return TRUE; -} - -void gaim_blist_load() { - GSList *accts; - char *user_dir = gaim_user_dir(); - char *filename; - char *msg; - - blist_safe_to_write = TRUE; - - if(!user_dir) - return; - - filename = g_build_filename(user_dir, "blist.xml", NULL); - - if(g_file_test(filename, G_FILE_TEST_EXISTS)) { - if(!gaim_blist_read(filename)) { - msg = g_strdup_printf(_("An error was encountered parsing your " - "buddy list. It has not been loaded.")); - do_error_dialog(_("Buddy List Error"), msg, GAIM_ERROR); - g_free(msg); - } - } else if(g_slist_length(gaim_accounts)) { - /* rob wants to inform the user that their buddy lists are - * being converted */ - msg = g_strdup_printf(_("Gaim is converting your old buddy lists " - "to a new format, which will now be located at %s"), - filename); - do_error_dialog(_("Converting Buddy List"), msg, GAIM_INFO); - g_free(msg); - - /* now, let gtk actually display the dialog before we start anything */ - while(gtk_events_pending()) - gtk_main_iteration(); - - /* read in the old lists, then save to the new format */ - for(accts = gaim_accounts; accts; accts = accts->next) { - do_import(accts->data, NULL); - } - gaim_blist_save(); - } - - g_free(filename); -} - -static void blist_print_group_settings(gpointer key, gpointer data, - gpointer user_data) { - char *key_val; - char *data_val; - FILE *file = user_data; - - if(!key || !data) - return; - - key_val = g_markup_escape_text(key, -1); - data_val = g_markup_escape_text(data, -1); - - fprintf(file, "\t\t\t%s\n", key_val, - data_val); - g_free(key_val); - g_free(data_val); -} - -static void blist_print_buddy_settings(gpointer key, gpointer data, - gpointer user_data) { - char *key_val; - char *data_val; - FILE *file = user_data; - - if(!key || !data) - return; - - key_val = g_markup_escape_text(key, -1); - data_val = g_markup_escape_text(data, -1); - - fprintf(file, "\t\t\t\t\t%s\n", key_val, - data_val); - g_free(key_val); - g_free(data_val); -} - -static void gaim_blist_write(FILE *file, struct gaim_account *exp_acct) { - GSList *accounts, *buds; - GaimBlistNode *gnode,*bnode; - struct group *group; - struct buddy *bud; - fprintf(file, "\n"); - fprintf(file, "\n"); - fprintf(file, "\t\n"); - - for(gnode = gaimbuddylist->root; gnode; gnode = gnode->next) { - if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) - continue; - group = (struct group *)gnode; - if(!exp_acct || gaim_group_on_account(group, exp_acct)) { - char *group_name = g_markup_escape_text(group->name, -1); - fprintf(file, "\t\t\n", group_name); - g_hash_table_foreach(group->settings, blist_print_group_settings, file); - for(bnode = gnode->child; bnode; bnode = bnode->next) { - if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) - continue; - bud = (struct buddy *)bnode; - if(!exp_acct || bud->account == exp_acct) { - char *bud_name = g_markup_escape_text(bud->name, -1); - char *bud_alias = NULL; - char *acct_name = g_markup_escape_text(bud->account->username, -1); - if(bud->alias) - bud_alias= g_markup_escape_text(bud->alias, -1); - fprintf(file, "\t\t\t\n", - bud_alias ? bud_alias : bud_name); - fprintf(file, "\t\t\t\t\n", bud->account->protocol, - acct_name); - fprintf(file, "\t\t\t\t\t%s\n", bud_name); - if(bud_alias) { - fprintf(file, "\t\t\t\t\t%s\n", - bud_alias); - } - g_hash_table_foreach(bud->settings, - blist_print_buddy_settings, file); - fprintf(file, "\t\t\t\t\n"); - fprintf(file, "\t\t\t\n"); - g_free(bud_name); - g_free(bud_alias); - g_free(acct_name); - } - } - fprintf(file, "\t\t\n"); - g_free(group_name); - } - } - - fprintf(file, "\t\n"); - fprintf(file, "\t\n"); - - for(accounts = gaim_accounts; accounts; accounts = accounts->next) { - struct gaim_account *account = accounts->data; - char *acct_name = g_markup_escape_text(account->username, -1); - if(!exp_acct || account == exp_acct) { - fprintf(file, "\t\t\n", account->protocol, acct_name, account->permdeny); - for(buds = account->permit; buds; buds = buds->next) { - char *bud_name = g_markup_escape_text(buds->data, -1); - fprintf(file, "\t\t\t%s\n", bud_name); - g_free(bud_name); - } - for(buds = account->deny; buds; buds = buds->next) { - char *bud_name = g_markup_escape_text(buds->data, -1); - fprintf(file, "\t\t\t%s\n", bud_name); - g_free(bud_name); - } - fprintf(file, "\t\t\n"); - } - g_free(acct_name); - } - - fprintf(file, "\t\n"); - fprintf(file, "\n"); -} - -void gaim_blist_save() { - FILE *file; - char *user_dir = gaim_user_dir(); - char *filename; - char *filename_real; - - if(!user_dir) - return; - if(!blist_safe_to_write) { - gaim_debug(GAIM_DEBUG_WARNING, "blist save", - "AHH!! Tried to write the blist before we read it!\n"); - return; - } - - file = fopen(user_dir, "r"); - if(!file) - mkdir(user_dir, S_IRUSR | S_IWUSR | S_IXUSR); - else - fclose(file); - - filename = g_build_filename(user_dir, "blist.xml.save", NULL); - - if((file = fopen(filename, "w"))) { - gaim_blist_write(file, NULL); - fclose(file); - chmod(filename, S_IRUSR | S_IWUSR); - } else { - gaim_debug(GAIM_DEBUG_ERROR, "blist save", "Unable to write %s\n", - filename); - } - - filename_real = g_build_filename(user_dir, "blist.xml", NULL); - - if(rename(filename, filename_real) < 0) - gaim_debug(GAIM_DEBUG_ERROR, "blist save", - "Error renaming %s to %s\n", filename, filename_real); - - - g_free(filename); - g_free(filename_real); -} - -gboolean gaim_privacy_permit_add(struct gaim_account *account, const char *who) { - GSList *d = account->permit; - char *n = g_strdup(normalize(who)); - while(d) { - if(!gaim_utf8_strcasecmp(n, normalize(d->data))) - break; - d = d->next; - } - g_free(n); - if(!d) { - account->permit = g_slist_append(account->permit, g_strdup(who)); - return TRUE; - } - - return FALSE; -} - -gboolean gaim_privacy_permit_remove(struct gaim_account *account, const char *who) { - GSList *d = account->permit; - char *n = g_strdup(normalize(who)); - while(d) { - if(!gaim_utf8_strcasecmp(n, normalize(d->data))) - break; - d = d->next; - } - g_free(n); - if(d) { - account->permit = g_slist_remove(account->permit, d->data); - g_free(d->data); - return TRUE; - } - return FALSE; -} - -gboolean gaim_privacy_deny_add(struct gaim_account *account, const char *who) { - GSList *d = account->deny; - char *n = g_strdup(normalize(who)); - while(d) { - if(!gaim_utf8_strcasecmp(n, normalize(d->data))) - break; - d = d->next; - } - g_free(n); - if(!d) { - account->deny = g_slist_append(account->deny, g_strdup(who)); - return TRUE; - } - - return FALSE; -} - -gboolean gaim_privacy_deny_remove(struct gaim_account *account, const char *who) { - GSList *d = account->deny; - char *n = g_strdup(normalize(who)); - while(d) { - if(!gaim_utf8_strcasecmp(n, normalize(d->data))) - break; - d = d->next; - } - g_free(n); - if(d) { - account->deny = g_slist_remove(account->deny, d->data); - g_free(d->data); - return TRUE; - } - return FALSE; -} - -void gaim_group_set_setting(struct group *g, const char *key, - const char *value) { - if(!g) - return; - g_hash_table_replace(g->settings, g_strdup(key), g_strdup(value)); -} - -char *gaim_group_get_setting(struct group *g, const char *key) { - if(!g) - return NULL; - return g_strdup(g_hash_table_lookup(g->settings, key)); -} - -void gaim_buddy_set_setting(struct buddy *b, const char *key, - const char *value) { - if(!b) - return; - g_hash_table_replace(b->settings, g_strdup(key), g_strdup(value)); -} - -char *gaim_buddy_get_setting(struct buddy *b, const char *key) { - if(!b) - return NULL; - return g_strdup(g_hash_table_lookup(b->settings, key)); -} - -void gaim_set_blist_ui_ops(struct gaim_blist_ui_ops *ops) -{ - blist_ui_ops = ops; -} - -struct gaim_blist_ui_ops * -gaim_get_blist_ui_ops(void) -{ - return blist_ui_ops; -} - -int gaim_blist_get_group_size(struct group *group, gboolean offline) { - GaimBlistNode *node; - int count = 0; - - if(!group) - return 0; - - for(node = group->node.child; node; node = node->next) { - if(GAIM_BLIST_NODE_IS_BUDDY(node)) { - struct buddy *b = (struct buddy *)node; - if(b->account->gc || offline) - count++; - } - } - - return count; -} - -int gaim_blist_get_group_online_count(struct group *group) { - GaimBlistNode *node; - int count = 0; - - if(!group) - return 0; - - for(node = group->node.child; node; node = node->next) { - if(GAIM_BLIST_NODE_IS_BUDDY(node)) { - struct buddy *b = (struct buddy *)node; - if(GAIM_BUDDY_IS_ONLINE(b)) - count++; - } - } - - return count; -} - - diff -r 6d1707dc8c3d -r 1a53330dfd34 src/list.h --- a/src/list.h Sat Apr 26 17:36:52 2003 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,479 +0,0 @@ -/** - * @file list.h Buddy List API - * @ingroup core - * - * gaim - * - * Copyright (C) 2003, Sean Egan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -/* I can't believe I let ChipX86 inspire me to write good code. -Sean */ - -#ifndef _LIST_H_ -#define _LIST_H_ - -#include - -/**************************************************************************/ -/* Enumerations */ -/**************************************************************************/ -enum gaim_blist_node_type { - GAIM_BLIST_GROUP_NODE, - GAIM_BLIST_BUDDY_NODE, - GAIM_BLIST_OTHER_NODE, -}; - -#define GAIM_BLIST_NODE_IS_BUDDY(n) ((n)->type == GAIM_BLIST_BUDDY_NODE) -#define GAIM_BLIST_NODE_IS_GROUP(n) ((n)->type == GAIM_BLIST_GROUP_NODE) - -enum gaim_buddy_presence_state { - GAIM_BUDDY_SIGNING_OFF = -1, - GAIM_BUDDY_OFFLINE = 0, - GAIM_BUDDY_ONLINE, - GAIM_BUDDY_SIGNING_ON, -}; - -#define GAIM_BUDDY_IS_ONLINE(b) ((b)->present == GAIM_BUDDY_ONLINE || \ - (b)->present == GAIM_BUDDY_SIGNING_ON) - - -/**************************************************************************/ -/* Data Structures */ -/**************************************************************************/ - -typedef struct _GaimBlistNode GaimBlistNode; -/** - * A Buddy list node. This can represent a group, a buddy, or anything else. This is a base class for struct buddy and - * struct group and for anything else that wants to put itself in the buddy list. */ -struct _GaimBlistNode { - enum gaim_blist_node_type type; /**< The type of node this is */ - GaimBlistNode *prev; /**< The sibling before this buddy. */ - GaimBlistNode *next; /**< The sibling after this buddy. */ - GaimBlistNode *parent; /**< The parent of this node */ - GaimBlistNode *child; /**< The child of this node */ - void *ui_data; /**< The UI can put data here. */ -}; - -/** - * A buddy. This contains everything Gaim will ever need to know about someone on the buddy list. Everything. - */ -struct buddy { - GaimBlistNode node; /**< The node that this buddy inherits from */ - char *name; /**< The screenname of the buddy. */ - char *alias; /**< The user-set alias of the buddy */ - char *server_alias; /**< The server-specified alias of the buddy. (i.e. MSN "Friendly Names") */ - enum gaim_buddy_presence_state present; /**< This is 0 if the buddy appears offline, 1 if he appears online, and 2 if - he has recently signed on */ - int evil; /**< The warning level */ - time_t signon; /**< The time the buddy signed on. */ - int idle; /**< The time the buddy has been idle in minutes. */ - int uc; /**< This is a cryptic bitmask that makes sense only to the prpl. This will get changed */ - void *proto_data; /**< This allows the prpl to associate whatever data it wants with a buddy */ - struct gaim_account *account; /**< the account this buddy belongs to */ - GHashTable *settings; /**< per-buddy settings from the XML buddy list, set by plugins and the likes. */ - guint timer; /**< The timer handle. */ -}; - -/** - * A group. This contains everything Gaim will ever need to know about a group. - */ -struct group { - GaimBlistNode node; /**< The node that this group inherits from */ - char *name; /**< The name of this group. */ - GHashTable *settings; /**< per-group settings from the XML buddy list, set by plugins and the likes. */ -}; - - -/** - * The Buddy List - */ -struct gaim_buddy_list { - GaimBlistNode *root; /**< The first node in the buddy list */ - struct gaim_blist_ui_ops *ui_ops; /**< The UI operations for the buddy list */ - - void *ui_data; /**< UI-specific data. */ -}; - -/** - * Buddy list UI operations. - * - * Any UI representing a buddy list must assign a filled-out gaim_window_ops - * structure to the buddy list core. - */ -struct gaim_blist_ui_ops -{ - void (*new_list)(struct gaim_buddy_list *list); /**< Sets UI-specific data on a buddy list. */ - void (*new_node)(GaimBlistNode *node); /**< Sets UI-specific data on a node. */ - void (*show)(struct gaim_buddy_list *list); /**< The core will call this when its finished doing it's core stuff */ - void (*update)(struct gaim_buddy_list *list, - GaimBlistNode *node); /**< This will update a node in the buddy list. */ - void (*remove)(struct gaim_buddy_list *list, - GaimBlistNode *node); /**< This removes a node from the list */ - void (*destroy)(struct gaim_buddy_list *list); /**< When the list gets destroyed, this gets called to destroy the UI. */ - void (*set_visible)(struct gaim_buddy_list *list, - gboolean show); /**< Hides or unhides the buddy list */ - -}; - -/**************************************************************************/ -/** @name Buddy List API */ -/**************************************************************************/ -/*@{*/ - -/** - * Creates a new buddy list - */ -struct gaim_buddy_list *gaim_blist_new(); - -/** - * Sets the main buddy list. - * - * @return The main buddy list. - */ -void gaim_set_blist(struct gaim_buddy_list *blist); - -/** - * Returns the main buddy list. - * - * @return The main buddy list. - */ -struct gaim_buddy_list *gaim_get_blist(void); - -/** - * Shows the buddy list, creating a new one if necessary. - * - */ -void gaim_blist_show(); - - -/** - * Destroys the buddy list window. - */ -void gaim_blist_destroy(); - -/** - * Hides or unhides the buddy list. - * - * @param show Whether or not to show the buddy list - */ -void gaim_blist_set_visible(gboolean show); - -/** - * Updates a buddy's status. - * - * This needs to not take an int. - * - * @param buddy The buddy whose status has changed - * @param status The new status in cryptic prpl-understood code - */ -void gaim_blist_update_buddy_status(struct buddy *buddy, int status); - - -/** - * Updates a buddy's presence. - * - * @param buddy The buddy whose presence has changed - * @param presence The new presence - */ -void gaim_blist_update_buddy_presence(struct buddy *buddy, int presence); - - -/** - * Updates a buddy's idle time. - * - * @param buddy The buddy whose idle time has changed - * @param idle The buddy's idle time in minutes. - */ -void gaim_blist_update_buddy_idle(struct buddy *buddy, int idle); - - -/** - * Updates a buddy's warning level. - * - * @param buddy The buddy whose warning level has changed - * @param evil The warning level as an int from 0 to 100 (or higher, I guess... but that'd be weird) - */ -void gaim_blist_update_buddy_evil(struct buddy *buddy, int warning); - -/** - * Updates a buddy's icon. - * - * @param buddy The buddy whose buddy icon has changed - */ -void gaim_blist_update_buddy_icon(struct buddy *buddy); - - - -/** - * Renames a buddy in the buddy list. - * - * @param buddy The buddy whose name will be changed. - * @param name The new name of the buddy. - */ -void gaim_blist_rename_buddy(struct buddy *buddy, const char *name); - - -/** - * Aliases a buddy in the buddy list. - * - * @param buddy The buddy whose alias will be changed. - * @param alias The buddy's alias. - */ -void gaim_blist_alias_buddy(struct buddy *buddy, const char *alias); - - -/** - * Renames a group - * - * @param group The group to rename - * @param name The new name - */ -void gaim_blist_rename_group(struct group *group, const char *name); - - -/** - * Creates a new buddy - * - * @param account The account this buddy will get added to - * @param screenname The screenname of the new buddy - * @param alias The alias of the new buddy (or NULL if unaliased) - * @return A newly allocated buddy - */ -struct buddy *gaim_buddy_new(struct gaim_account *account, const char *screenname, const char *alias); - -/** - * Adds a new buddy to the buddy list. - * - * The buddy will be inserted right after node or appended to the end - * of group if node is NULL. If both are NULL, the buddy will be added to - * the "Buddies" group. - * - * @param buddy The new buddy who gets added - * @param group The group to add the new buddy to. - * @param node The insertion point - */ -void gaim_blist_add_buddy(struct buddy *buddy, struct group *group, GaimBlistNode *node); - -/** - * Creates a new group - * - * You can't have more than one group with the same name. Sorry. If you pass this the - * name of a group that already exists, it will return that group. - * - * @param name The name of the new group - * @return A new group struct -*/ -struct group *gaim_group_new(const char *name); - -/** - * Adds a new group to the buddy list. - * - * The new group will be inserted after insert or appended to the end of - * the list if node is NULL. - * - * @param group The group to add the new buddy to. - * @param node The insertion point - */ -void gaim_blist_add_group(struct group *group, GaimBlistNode *node); - -/** - * Removes a buddy from the buddy list and frees the memory allocated to it. - * - * @param buddy The buddy to be removed - */ -void gaim_blist_remove_buddy(struct buddy *buddy); - -/** - * Removes a group from the buddy list and frees the memory allocated to it and to - * its children - * - * @param group The group to be removed - */ -void gaim_blist_remove_group(struct group *group); - -/** - * Returns the alias of a buddy. - * - * @param buddy The buddy whose name will be returned. - * @return The alias (if set), server alias (if option is set), or NULL. - */ -char *gaim_get_buddy_alias_only(struct buddy *buddy); - - -/** - * Returns the correct name to display for a buddy. - * - * @param buddy The buddy whose name will be returned. - * @return The alias (if set), server alias (if option is set), screenname, or "Unknown" - */ -char *gaim_get_buddy_alias(struct buddy *buddy); - -/** - * Finds the buddy struct given a screenname and an account - * - * @param name The buddy's screenname - * @param account The account this buddy belongs to - * @return The buddy or NULL if the buddy does not exist - */ -struct buddy *gaim_find_buddy(struct gaim_account *account, const char *name); - -/** - * Finds a group by name - * - * @param name The groups name - * @return The group or NULL if the group does not exist - */ -struct group *gaim_find_group(const char *name); - -/** - * Returns the group of which the buddy is a member. - * - * @param buddy The buddy - * @return The group or NULL if the buddy is not in a group - */ -struct group *gaim_find_buddys_group(struct buddy *buddy); - - -/** - * Returns a list of accounts that have buddies in this group - * - * @param group The group - * @return A list of gaim_accounts - */ -GSList *gaim_group_get_accounts(struct group *g); - -/** - * Determines whether an account owns any buddies in a given group - * - * @param g The group to search through. - * @param account The account. - */ -gboolean gaim_group_on_account(struct group *g, struct gaim_account *account); - -/** - * Called when an account gets signed off. Sets the presence of all the buddies to 0 - * and tells the UI to update them. - * - * @param account The account - */ -void gaim_blist_remove_account(struct gaim_account *account); - - -/** - * Determines the total size of a group - * - * @param group The group - * @param offline Count buddies in offline accounts - * @return The number of buddies in the group - */ -int gaim_blist_get_group_size(struct group *group, gboolean offline); - -/** - * Determines the number of online buddies in a group - * - * @param group The group - * @return The number of online buddies in the group, or 0 if the group is NULL - */ -int gaim_blist_get_group_online_count(struct group *group); - -/*@}*/ - -/****************************************************************************************/ -/** @name Buddy list file management API */ -/****************************************************************************************/ - -/*@{*/ -/** - * Saves the buddy list to file - */ -void gaim_blist_save(); - -/** - * Parses the toc-style buddy list used in older versions of Gaim and for SSI in toc.c - * - * @param account This is the account that the buddies and groups from config will get added to - * @param config This is the toc-style buddy list data - */ -void parse_toc_buddy_list(struct gaim_account *account, char *config); - - -/** - * Loads the buddy list from ~/.gaim/blist.xml. - */ -void gaim_blist_load(); - -/** - * Associates some data with the group in the xml buddy list - * - * @param g The group the data is associated with - * @param key The key used to retrieve the data - * @param value The data to set - */ -void gaim_group_set_setting(struct group *g, const char *key, const char *value); - -/** - * Retrieves data from the XML buddy list set by gaim_group_set_setting()) - * - * @param g The group to retrieve data from - * @param key The key to retrieve the data with - * @return The associated data or NULL if no data is associated - */ -char *gaim_group_get_setting(struct group *g, const char *key); - - -/** - * Associates some data with the buddy in the xml buddy list - * - * @param b The buddy the data is associated with - * @param key The key used to retrieve the data - * @param value The data to set - */ -void gaim_buddy_set_setting(struct buddy *b, const char *key, const char *value); - -/** - * Retrieves data from the XML buddy list set by gaim_buddy_set_setting()) - * - * @param b The buddy to retrieve data from - * @param key The key to retrieve the data with - * @return The associated data or NULL if no data is associated - */ -char *gaim_buddy_get_setting(struct buddy *b, const char *key); - -/*@}*/ - -/**************************************************************************/ -/** @name UI Registration Functions */ -/**************************************************************************/ -/*@{*/ - -/** - * Sets the UI operations structure to be used for the buddy list. - * - * @param ops The ops struct. - */ -void gaim_set_blist_ui_ops(struct gaim_blist_ui_ops *ops); - -/** - * Returns the UI operations structure to be used for the buddy list. - * - * @return The UI operations structure. - */ -struct gaim_blist_ui_ops *gaim_get_blist_ui_ops(void); - -/*@}*/ - -#endif /* _LIST_H_ */ diff -r 6d1707dc8c3d -r 1a53330dfd34 src/main.c --- a/src/main.c Sat Apr 26 17:36:52 2003 +0000 +++ b/src/main.c Sat Apr 26 17:56:23 2003 +0000 @@ -50,7 +50,7 @@ #include "sound.h" #include "gaim.h" #include "gaim-socket.h" -#include "gtklist.h" +#include "gtkblist.h" #include "gtkdebug.h" #if HAVE_SIGNAL_H #include diff -r 6d1707dc8c3d -r 1a53330dfd34 src/multi.c --- a/src/multi.c Sat Apr 26 17:36:52 2003 +0000 +++ b/src/multi.c Sat Apr 26 17:56:23 2003 +0000 @@ -27,7 +27,7 @@ #include "multi.h" #include "gaim.h" #include "conversation.h" -#include "gtklist.h" +#include "gtkblist.h" #include "gaim-disclosure.h" #ifdef _WIN32 diff -r 6d1707dc8c3d -r 1a53330dfd34 src/prefs.c --- a/src/prefs.c Sat Apr 26 17:36:52 2003 +0000 +++ b/src/prefs.c Sat Apr 26 17:56:23 2003 +0000 @@ -36,7 +36,7 @@ #include #include "gtkimhtml.h" #include "gaim.h" -#include "gtklist.h" +#include "gtkblist.h" #include "gtkplugin.h" #include "prpl.h" #include "proxy.h" diff -r 6d1707dc8c3d -r 1a53330dfd34 src/prpl.c --- a/src/prpl.c Sat Apr 26 17:36:52 2003 +0000 +++ b/src/prpl.c Sat Apr 26 17:56:23 2003 +0000 @@ -21,7 +21,7 @@ #include "gaim.h" #include "gtkutils.h" -#include "gtklist.h" +#include "gtkblist.h" #include "prpl.h" #include #include