Mercurial > pidgin.yaz
view src/conversation.c @ 5105:4cfc49e31c2e
[gaim-migrate @ 5468]
David Brigada (jsi) writes:
" This patch changes GTK_WRAP_WORD to GTK_WRAP_WORD_CHAR
throughout the sources. That fixes a bug where long
single-word input into GtkTextViews and similar would
cause the textview to expand, causing the window to
expand. This fix depends on a patch to GTK+ that is in
current CVS but not yet released. To address the
backwards-compatibility issue, GTK_WRAP_WORD_CHAR is
#defined to GTK_WRAP_WORD when not defined by GTK+'s
header files.
The only problem I can forsee is binary compatibility;
if Gaim is compiled under a GTK+ CVS (or a future
release) environment, and then run under a GTK+ 2.2.1
or earlier environment, behavior is undefined; the text
area will probably not wrap at all. However, source
compatibility is maintained."
basically, gaim will start wrapping text in any version of gtk2 that supports it,
and should work exactly the same in any version that still has the bug.
many thanks to David for his hard work.
committer: Tailor Script <tailor@pidgin.im>
author | Luke Schierer <lschiere@pidgin.im> |
---|---|
date | Fri, 11 Apr 2003 04:23:55 +0000 |
parents | bfcf72c5a930 |
children | 381da05cb5ed |
line wrap: on
line source
/* * gaim * * Copyright (C) 2002-2003, Christian Hammond <chipx86@gnupdate.org> * * 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 <config.h> #endif #include <string.h> #include <stdlib.h> #include <sys/stat.h> #include <errno.h> #include <ctype.h> #include "conversation.h" #include "gaim.h" #include "prpl.h" #ifdef _WIN32 #include "win32dep.h" #endif struct ConvPlacementData { char *name; gaim_conv_placement_fnc fnc; }; #define SEND_TYPED_TIMEOUT 5000 static struct gaim_window_ui_ops *win_ui_ops = NULL; static GList *conversations = NULL; static GList *ims = NULL; static GList *chats = NULL; static GList *windows = NULL; static GList *conv_placement_fncs = NULL; static gaim_conv_placement_fnc place_conv = NULL; static int place_conv_index = -1; static gint insertname_compare(gconstpointer one, gconstpointer two) { const char *a = (const char *)one; const char *b = (const char *)two; if (*a == '@') { if (*b != '@') return -1; return strcasecmp(a + 1, b + 1); } else if (*a == '%') { if (*b != '%') return -1; return strcasecmp(a + 1, b + 1); } else if (*a == '+') { if (*b == '@') return 1; if (*b != '+') return -1; return strcasecmp(a + 1, b + 1); } else if (*b == '@' || *b == '%' || *b == '+') return 1; return strcasecmp(a, b); } static gboolean find_nick(struct gaim_connection *gc, const char *message) { char *msg, *who, *p; int n; msg = g_utf8_strdown(message, -1); who = g_utf8_strdown(gc->username, -1); n = strlen(who); if ((p = strstr(msg, who)) != NULL) { if ((p == msg || !isalnum(*(p - 1))) && !isalnum(*(p + n))) { g_free(who); g_free(msg); return TRUE; } } g_free(who); if (!gaim_utf8_strcasecmp(gc->username, gc->displayname)) { g_free(msg); return FALSE; } who = g_utf8_strdown(gc->displayname, -1); n = strlen(who); if (n > 0 && (p = strstr(msg, who)) != NULL) { if ((p == msg || !isalnum(*(p - 1))) && !isalnum(*(p + n))) { g_free(who); g_free(msg); return TRUE; } } g_free(who); g_free(msg); return FALSE; } static gboolean reset_typing(gpointer data) { char *name = (char *)data; struct gaim_conversation *c = gaim_find_conversation(name); struct gaim_im *im; if (!c) return FALSE; im = GAIM_IM(c); gaim_im_set_typing_state(im, NOT_TYPING); gaim_im_update_typing(im); gaim_im_stop_typing_timeout(im); return FALSE; } static gboolean send_typed(gpointer data) { struct gaim_conversation *conv = (struct gaim_conversation *)data; struct gaim_connection *gc; const char *name; gc = gaim_conversation_get_gc(conv); name = gaim_conversation_get_name(conv); if (conv != NULL && gc != NULL && name != NULL) { gaim_im_set_type_again(GAIM_IM(conv), TRUE); /* XXX Somebody add const stuff! */ serv_send_typing(gc, (char *)name, TYPED); debug_printf("typed...\n"); } return FALSE; } static void common_send(struct gaim_conversation *conv, const char *message) { GaimConversationType type; struct gaim_connection *gc; struct gaim_conversation_ui_ops *ops; char *buf, *buf2, *buffy; gulong length = 0; gboolean binary = FALSE; int plugin_return; int limit; int err = 0; GList *first; if ((gc = gaim_conversation_get_gc(conv)) == NULL) return; type = gaim_conversation_get_type(conv); ops = gaim_conversation_get_ui_ops(conv); limit = 32 * 1024; /* You shouldn't be sending more than 32K in your messages. That's a book. */ buf = g_malloc(limit); strncpy(buf, message, limit); if (strlen(buf) == 0) { g_free(buf); return; } first = g_list_first(conv->send_history); if (first->data) g_free(first->data); first->data = g_strdup(buf); conv->send_history = g_list_prepend(first, NULL); buf2 = g_malloc(limit); if (gc->flags & OPT_CONN_HTML) { if (convo_options & OPT_CONVO_SEND_LINKS) linkify_text(buf); } buffy = g_strdup(buf); plugin_return = plugin_event( (type == GAIM_CONV_IM ? event_im_send : event_chat_send), gc, (type == GAIM_CONV_IM ? gaim_conversation_get_name(conv) : (void *)gaim_chat_get_id(GAIM_CHAT(conv))), &buffy); if (buffy == NULL) { g_free(buf2); g_free(buf); return; } if (plugin_return) { g_free(buffy); g_free(buf2); g_free(buf); return; } strncpy(buf, buffy, limit); g_free(buffy); if (type == GAIM_CONV_IM) { struct gaim_im *im = GAIM_IM(conv); buffy = g_strdup(buf); plugin_event(event_im_displayed_sent, gc, gaim_conversation_get_name(conv), &buffy); if (buffy != NULL) { int imflags = 0; if (conv->u.im->images != NULL) { int id = 0, offset = 0; char *bigbuf = NULL; GSList *tmplist; for (tmplist = conv->u.im->images; tmplist != NULL; tmplist = tmplist->next) { char *img_filename = (char *)tmplist->data; FILE *imgfile; char *filename, *c; struct stat st; char imgtag[1024]; id++; if (stat(img_filename, &st) != 0) { debug_printf("Could not stat %s\n", (char *)img_filename); continue; } /* * Here we check to make sure the user still wants to send * the image. He may have deleted the <img> tag in which * case we don't want to send the binary data. */ filename = img_filename; while ((c = strchr(filename, '/')) != NULL) filename = c + 1; g_snprintf(imgtag, sizeof(imgtag), "<IMG SRC=\"file://%s\" ID=\"%d\" " "DATASIZE=\"%d\">", filename, id, (int)st.st_size); if (strstr(buffy, imgtag) == 0) { debug_printf("Not sending image: %s\n", img_filename); continue; } if (!binary) { length = strlen(buffy) + strlen("<BINARY></BINARY>"); bigbuf = g_malloc(length + 1); g_snprintf(bigbuf, strlen(buffy) + strlen("<BINARY> ") + 1, "%s<BINARY>", buffy); offset = strlen(buffy) + strlen("<BINARY>"); binary = TRUE; } g_snprintf(imgtag, sizeof(imgtag), "<DATA ID=\"%d\" SIZE=\"%d\">", id, (int)st.st_size); length += strlen(imgtag) + st.st_size + strlen("</DATA>"); bigbuf = g_realloc(bigbuf, length + 1); if ((imgfile = fopen(img_filename, "r")) == NULL) { debug_printf("Could not open %s\n", img_filename); continue; } strncpy(bigbuf + offset, imgtag, strlen(imgtag) + 1); offset += strlen(imgtag); offset += fread(bigbuf + offset, 1, st.st_size, imgfile); fclose(imgfile); strncpy(bigbuf + offset, "</DATA>", strlen("</DATA>") + 1); offset += strlen("</DATA>"); } if (binary) { strncpy(bigbuf + offset, "</BINARY>", strlen("<BINARY>") + 1); err = serv_send_im(gc, (char *)gaim_conversation_get_name(conv), bigbuf, length, imflags); } else err = serv_send_im(gc, (char *)gaim_conversation_get_name(conv), buffy, -1, imflags); if (err > 0) { GSList *tempy; for (tempy = conv->u.im->images; tempy != NULL; tempy = tempy->next) { g_free(tempy->data); } g_slist_free(conv->u.im->images); conv->u.im->images = NULL; if (binary) gaim_im_write(im, NULL, bigbuf, length, WFLAG_SEND, time(NULL)); else gaim_im_write(im, NULL, buffy, -1, WFLAG_SEND, time(NULL)); if (im_options & OPT_IM_POPDOWN) gaim_window_hide(gaim_conversation_get_window(conv)); } if (binary) g_free(bigbuf); } else { err = serv_send_im(gc, (char *)gaim_conversation_get_name(conv), buffy, -1, imflags); if (err > 0) { gaim_im_write(im, NULL, buf, -1, WFLAG_SEND, time(NULL)); if (im_options & OPT_IM_POPDOWN) gaim_window_hide(gaim_conversation_get_window(conv)); } } g_free(buffy); } } else { err = serv_chat_send(gc, gaim_chat_get_id(GAIM_CHAT(conv)), buf); } g_free(buf2); g_free(buf); if (err < 0) { if (err == -E2BIG) do_error_dialog(_("Unable to send message. " "The message is too large."), NULL, GAIM_ERROR); else if (err == -ENOTCONN) debug_printf("Not yet connected.\n"); else do_error_dialog(_("Unable to send message."), NULL, GAIM_ERROR); } else { if (err > 0 && (away_options & OPT_AWAY_BACK_ON_IM)) { if (awaymessage != NULL) { do_im_back(); } else if (gc->away) { serv_set_away(gc, GAIM_AWAY_CUSTOM, NULL); } } } } static void update_conv_indexes(struct gaim_window *win) { GList *l; int i; for (l = gaim_window_get_conversations(win), i = 0; l != NULL; l = l->next, i++) { struct gaim_conversation *conv = (struct gaim_conversation *)l->data; conv->conversation_pos = i; } } struct gaim_window * gaim_window_new(void) { struct gaim_window *win; win = g_malloc0(sizeof(struct gaim_window)); win->ui_ops = gaim_get_win_ui_ops(); if (win->ui_ops != NULL && win->ui_ops->new_window != NULL) win->ui_ops->new_window(win); windows = g_list_append(windows, win); return win; } void gaim_window_destroy(struct gaim_window *win) { struct gaim_window_ui_ops *ops; GList *node; if (win == NULL) return; ops = gaim_window_get_ui_ops(win); /* * If there are any conversations in this, destroy them all. The last * conversation will call gaim_window_destroy(), but this time, this * check will fail and the window will actually be destroyed. * * This is needed because chats may not close right away. They may * wait for notification first. When they get that, the window is * already destroyed, and gaim either crashes or spits out gtk warnings. * The problem is fixed with this check. */ if (gaim_window_get_conversation_count(win) > 0) { node = g_list_first(gaim_window_get_conversations(win)); while(node != NULL) { struct gaim_conversation *conv = node->data; node = g_list_next(node); gaim_conversation_destroy(conv); } } else { if (ops != NULL && ops->destroy_window != NULL) ops->destroy_window(win); g_list_free(gaim_window_get_conversations(win)); windows = g_list_remove(windows, win); g_free(win); } } void gaim_window_show(struct gaim_window *win) { struct gaim_window_ui_ops *ops; if (win == NULL) return; ops = gaim_window_get_ui_ops(win); if (ops == NULL || ops->show == NULL) return; ops->show(win); } void gaim_window_hide(struct gaim_window *win) { struct gaim_window_ui_ops *ops; if (win == NULL) return; ops = gaim_window_get_ui_ops(win); if (ops == NULL || ops->hide == NULL) return; ops->hide(win); } void gaim_window_raise(struct gaim_window *win) { struct gaim_window_ui_ops *ops; if (win == NULL) return; ops = gaim_window_get_ui_ops(win); if (ops == NULL || ops->raise == NULL) return; ops->raise(win); } void gaim_window_flash(struct gaim_window *win) { struct gaim_window_ui_ops *ops; if (win == NULL) return; ops = gaim_window_get_ui_ops(win); if (ops == NULL || ops->flash == NULL) return; ops->flash(win); } void gaim_window_set_ui_ops(struct gaim_window *win, struct gaim_window_ui_ops *ops) { struct gaim_conversation_ui_ops *conv_ops = NULL; GList *l; if (win == NULL || win->ui_ops == ops) return; if (ops != NULL) { if (ops->get_conversation_ui_ops != NULL) conv_ops = ops->get_conversation_ui_ops(); } if (win->ui_ops != NULL) { if (win->ui_ops->destroy_window != NULL) win->ui_ops->destroy_window(win); } win->ui_ops = ops; if (win->ui_ops != NULL) { if (win->ui_ops->new_window != NULL) win->ui_ops->new_window(win); } for (l = gaim_window_get_conversations(win); l != NULL; l = l->next) { struct gaim_conversation *conv = (struct gaim_conversation *)l; gaim_conversation_set_ui_ops(conv, conv_ops); if (win->ui_ops != NULL && win->ui_ops->add_conversation != NULL) win->ui_ops->add_conversation(win, conv); } } struct gaim_window_ui_ops * gaim_window_get_ui_ops(const struct gaim_window *win) { if (win == NULL) return NULL; return win->ui_ops; } int gaim_window_add_conversation(struct gaim_window *win, struct gaim_conversation *conv) { struct gaim_window_ui_ops *ops; if (win == NULL || conv == NULL) return -1; if (gaim_conversation_get_window(conv) != NULL) { gaim_window_remove_conversation( gaim_conversation_get_window(conv), gaim_conversation_get_index(conv)); } ops = gaim_window_get_ui_ops(win); win->conversations = g_list_append(win->conversations, conv); win->conversation_count++; conv->conversation_pos = win->conversation_count - 1; if (ops != NULL) { conv->window = win; if (ops->get_conversation_ui_ops != NULL) gaim_conversation_set_ui_ops(conv, ops->get_conversation_ui_ops()); if (ops->add_conversation != NULL) ops->add_conversation(win, conv); } return win->conversation_count - 1; } struct gaim_conversation * gaim_window_remove_conversation(struct gaim_window *win, unsigned int index) { struct gaim_window_ui_ops *ops; struct gaim_conversation *conv; GList *node; if (win == NULL || index >= gaim_window_get_conversation_count(win)) return NULL; ops = gaim_window_get_ui_ops(win); node = g_list_nth(gaim_window_get_conversations(win), index); conv = (struct gaim_conversation *)node->data; if (ops != NULL && ops->remove_conversation != NULL) ops->remove_conversation(win, conv); win->conversations = g_list_remove_link(win->conversations, node); g_list_free_1(node); win->conversation_count--; conv->window = NULL; if (gaim_window_get_conversation_count(win) == 0) gaim_window_destroy(win); else { /* Change all the indexes. */ update_conv_indexes(win); } return conv; } void gaim_window_move_conversation(struct gaim_window *win, unsigned int index, unsigned int new_index) { struct gaim_window_ui_ops *ops; struct gaim_conversation *conv; GList *l; if (win == NULL || index >= gaim_window_get_conversation_count(win) || index == new_index) return; /* We can't move this past the last index. */ if (new_index > gaim_window_get_conversation_count(win)) new_index = gaim_window_get_conversation_count(win); /* Get the list item for this conversation at its current index. */ l = g_list_nth(gaim_window_get_conversations(win), index); if (l == NULL) { /* Should never happen. */ debug_printf("Misordered conversations list in window %p\n", win); return; } conv = (struct gaim_conversation *)l->data; /* Update the UI part of this. */ ops = gaim_window_get_ui_ops(win); if (ops != NULL && ops->move_conversation != NULL) ops->move_conversation(win, conv, new_index); if (new_index > index) new_index--; /* Remove the old one. */ win->conversations = g_list_delete_link(win->conversations, l); /* Insert it where it should go. */ win->conversations = g_list_insert(win->conversations, conv, new_index); update_conv_indexes(win); } struct gaim_conversation * gaim_window_get_conversation_at(const struct gaim_window *win, unsigned int index) { if (win == NULL || index >= gaim_window_get_conversation_count(win)) return NULL; return (struct gaim_conversation *)g_list_nth_data( gaim_window_get_conversations(win), index); } size_t gaim_window_get_conversation_count(const struct gaim_window *win) { if (win == NULL) return 0; return win->conversation_count; } void gaim_window_switch_conversation(struct gaim_window *win, unsigned int index) { struct gaim_window_ui_ops *ops; if (win == NULL || index < 0 || index >= gaim_window_get_conversation_count(win)) return; ops = gaim_window_get_ui_ops(win); if (ops != NULL && ops->switch_conversation != NULL) ops->switch_conversation(win, index); gaim_conversation_set_unseen( gaim_window_get_conversation_at(win, index), 0); } struct gaim_conversation * gaim_window_get_active_conversation(const struct gaim_window *win) { struct gaim_window_ui_ops *ops; if (win == NULL) return NULL; ops = gaim_window_get_ui_ops(win); if (ops != NULL && ops->get_active_index != NULL) return gaim_window_get_conversation_at(win, ops->get_active_index(win)); return NULL; } GList * gaim_window_get_conversations(const struct gaim_window *win) { if (win == NULL) return NULL; return win->conversations; } GList * gaim_get_windows(void) { return windows; } struct gaim_window * gaim_get_first_window_with_type(GaimConversationType type) { GList *wins, *convs; struct gaim_window *win; struct gaim_conversation *conv; if (type == GAIM_CONV_UNKNOWN) return NULL; for (wins = gaim_get_windows(); wins != NULL; wins = wins->next) { win = (struct gaim_window *)wins->data; for (convs = gaim_window_get_conversations(win); convs != NULL; convs = convs->next) { conv = (struct gaim_conversation *)convs->data; if (gaim_conversation_get_type(conv) == type) return win; } } return NULL; } struct gaim_window * gaim_get_last_window_with_type(GaimConversationType type) { GList *wins, *convs; struct gaim_window *win; struct gaim_conversation *conv; if (type == GAIM_CONV_UNKNOWN) return NULL; for (wins = g_list_last(gaim_get_windows()); wins != NULL; wins = wins->prev) { win = (struct gaim_window *)wins->data; for (convs = gaim_window_get_conversations(win); convs != NULL; convs = convs->next) { conv = (struct gaim_conversation *)convs->data; if (gaim_conversation_get_type(conv) == type) return win; } } return NULL; } /************************************************************************** * Conversation API **************************************************************************/ struct gaim_conversation * gaim_conversation_new(GaimConversationType type, struct gaim_account *account, const char *name) { struct gaim_conversation *conv; if (type == GAIM_CONV_UNKNOWN) return NULL; /* Check if this conversation already exists. */ if ((conv = gaim_find_conversation_with_account(name, account)) != NULL) return conv; conv = g_malloc0(sizeof(struct gaim_conversation)); conv->type = type; conv->account = account; conv->name = g_strdup(name); conv->title = g_strdup(name); conv->send_history = g_list_append(NULL, NULL); conv->history = g_string_new(""); conv->data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); if (type == GAIM_CONV_IM) { conv->u.im = g_malloc0(sizeof(struct gaim_im)); conv->u.im->conv = conv; ims = g_list_append(ims, conv); gaim_conversation_set_logging(conv, (logging_options & OPT_LOG_CONVOS)); } else if (type == GAIM_CONV_CHAT) { conv->u.chat = g_malloc0(sizeof(struct gaim_chat)); conv->u.chat->conv = conv; chats = g_list_append(chats, conv); gaim_conversation_set_logging(conv, (logging_options & OPT_LOG_CHATS)); } conversations = g_list_append(conversations, conv); /* Auto-set the title. */ gaim_conversation_autoset_title(conv); /* * Create a window if one does not exist. If it does, use the last * created window. */ if (windows == NULL || (type == GAIM_CONV_IM && !(im_options & OPT_IM_ONE_WINDOW)) || (type == GAIM_CONV_CHAT && !(chat_options & OPT_CHAT_ONE_WINDOW))) { struct gaim_window *win; win = gaim_window_new(); gaim_window_add_conversation(win, conv); /* Ensure the window is visible. */ gaim_window_show(win); } else { if (place_conv == NULL) gaim_conv_placement_set_active(0); place_conv(conv); } plugin_event(event_new_conversation, name); return conv; } void gaim_conversation_destroy(struct gaim_conversation *conv) { struct gaim_window *win; struct gaim_conversation_ui_ops *ops; struct gaim_connection *gc; const char *name; GList *node; if (conv == NULL) return; win = gaim_conversation_get_window(conv); ops = gaim_conversation_get_ui_ops(conv); gc = gaim_conversation_get_gc(conv); name = gaim_conversation_get_name(conv); if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) { if (!(misc_options & OPT_MISC_STEALTH_TYPING)) serv_send_typing(gc, (char *)name, NOT_TYPING); if (gc && gc->prpl->convo_closed != NULL) gc->prpl->convo_closed(gc, (char *)name); } else if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT) { /* * This is unfortunately necessary, because calling serv_chat_leave() * calls this gaim_conversation_destroy(), which leads to two calls * here.. We can't just return after this, because then it'll return * on the next pass. So, since serv_got_chat_left(), which is * eventually called from the prpl that serv_chat_leave() calls, * removes this conversation from the gc's buddy_chats list, we're * going to check to see if this exists in the list. If so, we want * to return after calling this, because it'll be called again. If not, * fall through, because it'll have already been removed, and we'd * be on the 2nd pass. * * Long paragraph. <-- Short sentence. * * -- ChipX86 */ if (gc && g_slist_find(gc->buddy_chats, conv) != NULL) { serv_chat_leave(gc, gaim_chat_get_id(GAIM_CHAT(conv))); return; } } plugin_event(event_del_conversation, conv); if (conv->name != NULL) g_free(conv->name); if (conv->title != NULL) g_free(conv->title); for (node = g_list_first(conv->send_history); node != NULL; node = g_list_next(node)) { if (node->data != NULL) g_free(node->data); } g_list_free(g_list_first(conv->send_history)); if (conv->history != NULL) g_string_free(conv->history, TRUE); conversations = g_list_remove(conversations, conv); if (conv->type == GAIM_CONV_IM) { GSList *snode; gaim_im_stop_typing_timeout(conv->u.im); gaim_im_stop_type_again_timeout(conv->u.im); for (snode = conv->u.im->images; snode != NULL; snode = snode->next) { if (snode->data != NULL) g_free(snode->data); } g_slist_free(conv->u.im->images); g_free(conv->u.im); ims = g_list_remove(ims, conv); } else if (conv->type == GAIM_CONV_CHAT) { for (node = conv->u.chat->in_room; node != NULL; node = node->next) { if (node->data != NULL) g_free(node->data); } for (node = conv->u.chat->ignored; node != NULL; node = node->next) { if (node->data != NULL) g_free(node->data); } g_list_free(conv->u.chat->in_room); g_list_free(conv->u.chat->ignored); if (conv->u.chat->who != NULL) g_free(conv->u.chat->who); if (conv->u.chat->topic != NULL) g_free(conv->u.chat->topic); g_free(conv->u.chat); chats = g_list_remove(chats, conv); } g_hash_table_destroy(conv->data); if (win != NULL) { gaim_window_remove_conversation(win, gaim_conversation_get_index(conv)); } if (ops != NULL && ops->destroy_conversation != NULL) ops->destroy_conversation(conv); g_free(conv); } GaimConversationType gaim_conversation_get_type(const struct gaim_conversation *conv) { if (conv == NULL) return GAIM_CONV_UNKNOWN; return conv->type; } void gaim_conversation_set_ui_ops(struct gaim_conversation *conv, struct gaim_conversation_ui_ops *ops) { if (conv == NULL || conv->ui_ops == ops) return; if (conv->ui_ops != NULL && conv->ui_ops->destroy_conversation != NULL) conv->ui_ops->destroy_conversation(conv); conv->ui_data = NULL; conv->ui_ops = ops; } struct gaim_conversation_ui_ops * gaim_conversation_get_ui_ops(struct gaim_conversation *conv) { if (conv == NULL) return NULL; return conv->ui_ops; } void gaim_conversation_set_account(struct gaim_conversation *conv, struct gaim_account *account) { if (conv == NULL || account == gaim_conversation_get_account(conv)) return; conv->account = account; gaim_conversation_update(conv, GAIM_CONV_UPDATE_ACCOUNT); } struct gaim_account * gaim_conversation_get_account(const struct gaim_conversation *conv) { if (conv == NULL) return NULL; return conv->account; } struct gaim_connection * gaim_conversation_get_gc(const struct gaim_conversation *conv) { struct gaim_account *account; if (conv == NULL) return NULL; account = gaim_conversation_get_account(conv); if (account == NULL) return NULL; return account->gc; } void gaim_conversation_set_title(struct gaim_conversation *conv, const char *title) { struct gaim_conversation_ui_ops *ops; if (conv == NULL || title == NULL) return; if (conv->title != NULL) g_free(conv->title); conv->title = g_strdup(title); ops = gaim_conversation_get_ui_ops(conv); if (ops != NULL && ops->set_title != NULL) ops->set_title(conv, conv->title); } const char * gaim_conversation_get_title(const struct gaim_conversation *conv) { if (conv == NULL) return NULL; return conv->title; } void gaim_conversation_autoset_title(struct gaim_conversation *conv) { struct gaim_account *account; struct buddy *b; const char *text, *name; if (conv == NULL) return; account = gaim_conversation_get_account(conv); name = gaim_conversation_get_name(conv); if (((im_options & OPT_IM_ALIAS_TAB) == OPT_IM_ALIAS_TAB) && account != NULL && ((b = gaim_find_buddy(account, name)) != NULL)) { text = gaim_get_buddy_alias(b); } else text = name; gaim_conversation_set_title(conv, text); } int gaim_conversation_get_index(const struct gaim_conversation *conv) { if (conv == NULL) return 0; return conv->conversation_pos; } void gaim_conversation_set_unseen(struct gaim_conversation *conv, GaimUnseenState state) { if (conv == NULL) return; conv->unseen = state; gaim_conversation_update(conv, GAIM_CONV_UPDATE_UNSEEN); } void gaim_conversation_foreach(void (*func)(struct gaim_conversation *conv)) { struct gaim_conversation *conv; GList *l; if (func == NULL) return; for (l = gaim_get_conversations(); l != NULL; l = l->next) { conv = (struct gaim_conversation *)l->data; func(conv); } } GaimUnseenState gaim_conversation_get_unseen(const struct gaim_conversation *conv) { if (conv == NULL) return 0; return conv->unseen; } const char * gaim_conversation_get_name(const struct gaim_conversation *conv) { if (conv == NULL) return NULL; return conv->name; } void gaim_conversation_set_logging(struct gaim_conversation *conv, gboolean log) { if (conv == NULL) return; conv->logging = log; gaim_conversation_update(conv, GAIM_CONV_UPDATE_LOGGING); } gboolean gaim_conversation_is_logging(const struct gaim_conversation *conv) { if (conv == NULL) return FALSE; return conv->logging; } GList * gaim_conversation_get_send_history(const struct gaim_conversation *conv) { if (conv == NULL) return NULL; return conv->send_history; } void gaim_conversation_set_history(struct gaim_conversation *conv, GString *history) { if (conv == NULL) return; conv->history = history; } GString * gaim_conversation_get_history(const struct gaim_conversation *conv) { if (conv == NULL) return NULL; return conv->history; } struct gaim_window * gaim_conversation_get_window(const struct gaim_conversation *conv) { if (conv == NULL) return NULL; return conv->window; } struct gaim_im * gaim_conversation_get_im_data(const struct gaim_conversation *conv) { if (conv == NULL) return NULL; if (gaim_conversation_get_type(conv) != GAIM_CONV_IM) return NULL; return conv->u.im; } struct gaim_chat * gaim_conversation_get_chat_data(const struct gaim_conversation *conv) { if (conv == NULL) return NULL; if (gaim_conversation_get_type(conv) != GAIM_CONV_CHAT) return NULL; return conv->u.chat; } void gaim_conversation_set_data(struct gaim_conversation *conv, const char *key, gpointer data) { if (conv == NULL || key == NULL) return; g_hash_table_replace(conv->data, g_strdup(key), data); } gpointer gaim_conversation_get_data(struct gaim_conversation *conv, const char *key) { if (conv == NULL || key == NULL) return NULL; return g_hash_table_lookup(conv->data, key); } GList * gaim_get_conversations(void) { return conversations; } GList * gaim_get_ims(void) { return ims; } GList * gaim_get_chats(void) { return chats; } struct gaim_conversation * gaim_find_conversation(const char *name) { struct gaim_conversation *c = NULL; char *cuser; GList *cnv; if (name == NULL) return NULL; cuser = g_strdup(normalize(name)); for (cnv = gaim_get_conversations(); cnv != NULL; cnv = cnv->next) { c = (struct gaim_conversation *)cnv->data; if (!gaim_utf8_strcasecmp(cuser, normalize(gaim_conversation_get_name(c)))) break; c = NULL; } g_free(cuser); return c; } struct gaim_conversation * gaim_find_conversation_with_account(const char *name, const struct gaim_account *account) { struct gaim_conversation *c = NULL; char *cuser; GList *cnv; if (name == NULL) return NULL; cuser = g_strdup(normalize(name)); for (cnv = gaim_get_conversations(); cnv != NULL; cnv = cnv->next) { c = (struct gaim_conversation *)cnv->data; if (!gaim_utf8_strcasecmp(cuser, normalize(gaim_conversation_get_name(c))) && account == gaim_conversation_get_account(c)) { break; } c = NULL; } g_free(cuser); return c; } void gaim_conversation_write(struct gaim_conversation *conv, const char *who, const char *message, size_t length, int flags, time_t mtime) { struct gaim_account *account; struct gaim_conversation_ui_ops *ops; struct gaim_window *win; struct buddy *b; GaimUnseenState unseen; /* int logging_font_options = 0; */ if (conv == NULL || message == NULL) return; ops = gaim_conversation_get_ui_ops(conv); if (ops == NULL || ops->write_conv == NULL) return; account = gaim_conversation_get_account(conv); if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT && (account->gc == NULL || !g_slist_find(account->gc->buddy_chats, conv))) return; if (gaim_conversation_get_type(conv) == GAIM_CONV_IM && !g_list_find(gaim_get_conversations(), conv)) return; if (gaim_conversation_get_type(conv) == GAIM_CONV_IM || !(account->gc->prpl->options & OPT_PROTO_UNIQUE_CHATNAME)) { if (who == NULL) { if (flags & WFLAG_SEND) { b = gaim_find_buddy(account, account->gc->username); if (b != NULL && strcmp(b->name, gaim_get_buddy_alias(b))) who = gaim_get_buddy_alias(b); else if (*account->alias) who = account->alias; else if (*account->gc->displayname) who = account->gc->displayname; else who = account->gc->username; } else { b = gaim_find_buddy(account, gaim_conversation_get_name(conv)); if (b != NULL) who = gaim_get_buddy_alias(b); else who = gaim_conversation_get_name(conv); } } else { b = gaim_find_buddy(account, who); if (b != NULL) who = gaim_get_buddy_alias(b); } } ops->write_conv(conv, who, message, length, flags, mtime); win = gaim_conversation_get_window(conv); if (!(flags & WFLAG_NOLOG) && ((gaim_conversation_get_type(conv) == GAIM_CONV_CHAT && (chat_options & OPT_CHAT_POPUP)) || (gaim_conversation_get_type(conv) == GAIM_CONV_IM && ((im_options & OPT_IM_POPUP) || (im_options & OPT_IM_POPDOWN))))) { gaim_window_show(win); } /* Tab highlighting */ if (!(flags & WFLAG_RECV) && !(flags & WFLAG_SYSTEM)) return; if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) { if ((flags & WFLAG_RECV) == WFLAG_RECV) gaim_im_set_typing_state(GAIM_IM(conv), NOT_TYPING); } if (gaim_window_get_active_conversation(win) != conv) { if ((flags & WFLAG_NICK) == WFLAG_NICK || gaim_conversation_get_unseen(conv) == GAIM_UNSEEN_NICK) unseen = GAIM_UNSEEN_NICK; else unseen = GAIM_UNSEEN_TEXT; } else unseen = GAIM_UNSEEN_NONE; gaim_conversation_set_unseen(conv, unseen); } void gaim_conversation_update_progress(struct gaim_conversation *conv, float percent) { struct gaim_conversation_ui_ops *ops; if (conv == NULL) return; if (percent < 0) percent = 0; /* * NOTE: A percent >= 1 indicates that the progress bar should be * closed. */ ops = gaim_conversation_get_ui_ops(conv); if (ops != NULL && ops->update_progress != NULL) ops->update_progress(conv, percent); } void gaim_conversation_update(struct gaim_conversation *conv, GaimConvUpdateType type) { struct gaim_conversation_ui_ops *ops; if (conv == NULL) return; ops = gaim_conversation_get_ui_ops(conv); if (ops != NULL && ops->updated != NULL) ops->updated(conv, type); } /************************************************************************** * IM Conversation API **************************************************************************/ struct gaim_conversation * gaim_im_get_conversation(struct gaim_im *im) { if (im == NULL) return NULL; return im->conv; } void gaim_im_set_typing_state(struct gaim_im *im, int state) { if (im == NULL) return; im->typing_state = state; } int gaim_im_get_typing_state(const struct gaim_im *im) { if (im == NULL) return 0; return im->typing_state; } void gaim_im_start_typing_timeout(struct gaim_im *im, int timeout) { struct gaim_conversation *conv; const char *name; if (im == NULL) return; if (im->typing_timeout > 0) gaim_im_stop_typing_timeout(im); conv = gaim_im_get_conversation(im); name = gaim_conversation_get_name(conv); im->typing_timeout = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, timeout * 1000, reset_typing, g_strdup(name), g_free); } void gaim_im_stop_typing_timeout(struct gaim_im *im) { if (im == NULL) return; if (im->typing_timeout == 0) return; g_source_remove(im->typing_timeout); im->typing_timeout = 0; } guint gaim_im_get_typing_timeout(const struct gaim_im *im) { if (im == NULL) return 0; return im->typing_timeout; } void gaim_im_set_type_again(struct gaim_im *im, time_t val) { if (im == NULL) return; im->type_again = val; } time_t gaim_im_get_type_again(const struct gaim_im *im) { if (im == NULL) return 0; return im->type_again; } void gaim_im_start_type_again_timeout(struct gaim_im *im) { if (im == NULL) return; im->type_again_timeout = g_timeout_add(SEND_TYPED_TIMEOUT, send_typed, gaim_im_get_conversation(im)); } void gaim_im_stop_type_again_timeout(struct gaim_im *im) { if (im == NULL) return; if (im->type_again_timeout == 0) return; g_source_remove(im->type_again_timeout); im->type_again_timeout = 0; } guint gaim_im_get_type_again_timeout(const struct gaim_im *im) { if (im == NULL) return 0; return im->type_again_timeout; } void gaim_im_update_typing(struct gaim_im *im) { if (im == NULL) return; gaim_conversation_update(gaim_im_get_conversation(im), GAIM_CONV_UPDATE_TYPING); } void gaim_im_write(struct gaim_im *im, const char *who, const char *message, size_t len, int flags, time_t mtime) { struct gaim_conversation *c; if (im == NULL || message == NULL) return; c = gaim_im_get_conversation(im); /* Raise the window, if specified in prefs. */ if (!(flags & WFLAG_NOLOG) & (im_options & OPT_IM_POPUP)) gaim_window_raise(gaim_conversation_get_window(c)); if (c->ui_ops != NULL && c->ui_ops->write_im != NULL) c->ui_ops->write_im(c, who, message, len, flags, mtime); else gaim_conversation_write(c, who, message, -1, flags, mtime); } void gaim_im_send(struct gaim_im *im, const char *message) { if (im == NULL || message == NULL) return; common_send(gaim_im_get_conversation(im), message); } /************************************************************************** * Chat Conversation API **************************************************************************/ struct gaim_conversation * gaim_chat_get_conversation(struct gaim_chat *chat) { if (chat == NULL) return NULL; return chat->conv; } GList * gaim_chat_set_users(struct gaim_chat *chat, GList *users) { if (chat == NULL) return NULL; chat->in_room = users; return users; } GList * gaim_chat_get_users(const struct gaim_chat *chat) { if (chat == NULL) return NULL; return chat->in_room; } void gaim_chat_ignore(struct gaim_chat *chat, const char *name) { if (chat == NULL || name == NULL) return; /* Make sure the user isn't already ignored. */ if (gaim_chat_is_user_ignored(chat, name)) return; gaim_chat_set_ignored(chat, g_list_append(gaim_chat_get_ignored(chat), g_strdup(name))); } void gaim_chat_unignore(struct gaim_chat *chat, const char *name) { GList *item; if (chat == NULL || name == NULL) return; /* Make sure the user is actually ignored. */ if (!gaim_chat_is_user_ignored(chat, name)) return; item = g_list_find(gaim_chat_get_ignored(chat), gaim_chat_get_ignored_user(chat, name)); gaim_chat_set_ignored(chat, g_list_remove_link(gaim_chat_get_ignored(chat), item)); g_free(item->data); g_list_free_1(item); } GList * gaim_chat_set_ignored(struct gaim_chat *chat, GList *ignored) { if (chat == NULL) return NULL; chat->ignored = ignored; return ignored; } GList * gaim_chat_get_ignored(const struct gaim_chat *chat) { if (chat == NULL) return NULL; return chat->ignored; } const char * gaim_chat_get_ignored_user(const struct gaim_chat *chat, const char *user) { GList *ignored; if (chat == NULL || user == NULL) return NULL; for (ignored = gaim_chat_get_ignored(chat); ignored != NULL; ignored = ignored->next) { const char *ign = (const char *)ignored->data; if (!gaim_utf8_strcasecmp(user, ign) || ((*ign == '+' || *ign == '%') && !gaim_utf8_strcasecmp(user, ign + 1))) return ign; if (*ign == '@') { ign++; if ((*ign == '+' && !gaim_utf8_strcasecmp(user, ign + 1)) || (*ign != '+' && !gaim_utf8_strcasecmp(user, ign))) return ign; } } return NULL; } gboolean gaim_chat_is_user_ignored(const struct gaim_chat *chat, const char *user) { if (chat == NULL || user == NULL) return FALSE; return (gaim_chat_get_ignored_user(chat, user) != NULL); } void gaim_chat_set_topic(struct gaim_chat *chat, const char *who, const char *topic) { if (chat == NULL) return; if (chat->who != NULL) free(chat->who); if (chat->topic != NULL) free(chat->topic); chat->who = (who == NULL ? NULL : g_strdup(who)); chat->topic = (topic == NULL ? NULL : g_strdup(topic)); gaim_conversation_update(gaim_chat_get_conversation(chat), GAIM_CONV_UPDATE_TOPIC); } const char * gaim_chat_get_topic(const struct gaim_chat *chat) { if (chat == NULL) return NULL; return chat->topic; } void gaim_chat_set_id(struct gaim_chat *chat, int id) { if (chat == NULL) return; chat->id = id; } int gaim_chat_get_id(const struct gaim_chat *chat) { if (chat == NULL) return -1; return chat->id; } void gaim_chat_write(struct gaim_chat *chat, const char *who, const char *message, int flags, time_t mtime) { struct gaim_conversation *conv; struct gaim_connection *gc; if (chat == NULL || who == NULL || message == NULL) return; conv = gaim_chat_get_conversation(chat); gc = gaim_conversation_get_gc(conv); /* Don't display this if the person who wrote it is ignored. */ if (gaim_chat_is_user_ignored(chat, who)) return; /* Raise the window, if specified in prefs. */ if (!(flags & WFLAG_NOLOG) & (chat_options & OPT_CHAT_POPUP)) gaim_window_raise(gaim_conversation_get_window(conv)); if (!(flags & WFLAG_WHISPER)) { char *str; str = g_strdup(normalize(who)); if (!gaim_utf8_strcasecmp(str, normalize(gc->username)) || !gaim_utf8_strcasecmp(str, normalize(gc->displayname))) { flags |= WFLAG_SEND; } else { flags |= WFLAG_RECV; if (find_nick(gc, message)) flags |= WFLAG_NICK; } g_free(str); } /* Pass this on to either the ops structure or the default write func. */ if (conv->ui_ops != NULL && conv->ui_ops->write_chat != NULL) conv->ui_ops->write_chat(conv, who, message, flags, mtime); else gaim_conversation_write(conv, who, message, -1, flags, mtime); } void gaim_chat_send(struct gaim_chat *chat, const char *message) { if (chat == NULL || message == NULL) return; common_send(gaim_chat_get_conversation(chat), message); } void gaim_chat_add_user(struct gaim_chat *chat, const char *user, const char *extra_msg) { struct gaim_conversation *conv; struct gaim_conversation_ui_ops *ops; char tmp[BUF_LONG]; if (chat == NULL || user == NULL) return; conv = gaim_chat_get_conversation(chat); ops = gaim_conversation_get_ui_ops(conv); gaim_chat_set_users(chat, g_list_insert_sorted(gaim_chat_get_users(chat), g_strdup(user), insertname_compare)); plugin_event(event_chat_buddy_join, gaim_conversation_get_gc(conv), gaim_chat_get_id(chat), user); if (ops != NULL && ops->chat_add_user != NULL) ops->chat_add_user(conv, user); if (chat_options & OPT_CHAT_LOGON) { if (extra_msg == NULL) g_snprintf(tmp, sizeof(tmp), _("%s entered the room."), user); else g_snprintf(tmp, sizeof(tmp), _("%s [<I>%s</I>] entered the room."), user, extra_msg); gaim_conversation_write(conv, NULL, tmp, -1, WFLAG_SYSTEM, time(NULL)); } } void gaim_chat_rename_user(struct gaim_chat *chat, const char *old_user, const char *new_user) { struct gaim_conversation *conv; struct gaim_conversation_ui_ops *ops; char tmp[BUF_LONG]; GList *names; if (chat == NULL || old_user == NULL || new_user == NULL) return; conv = gaim_chat_get_conversation(chat); ops = gaim_conversation_get_ui_ops(conv); gaim_chat_set_users(chat, g_list_insert_sorted(gaim_chat_get_users(chat), g_strdup(new_user), insertname_compare)); if (ops != NULL && ops->chat_rename_user != NULL) ops->chat_rename_user(conv, old_user, new_user); for (names = gaim_chat_get_users(chat); names != NULL; names = names->next) { if (!gaim_utf8_strcasecmp((char *)names->data, old_user)) { gaim_chat_set_users(chat, g_list_remove(gaim_chat_get_users(chat), names->data)); break; } } if (gaim_chat_is_user_ignored(chat, old_user)) { gaim_chat_unignore(chat, old_user); gaim_chat_ignore(chat, new_user); } else if (gaim_chat_is_user_ignored(chat, new_user)) gaim_chat_unignore(chat, new_user); if (chat_options & OPT_CHAT_LOGON) { g_snprintf(tmp, sizeof(tmp), _("%s is now known as %s"), old_user, new_user); gaim_conversation_write(conv, NULL, tmp, -1, WFLAG_SYSTEM, time(NULL)); } } void gaim_chat_remove_user(struct gaim_chat *chat, const char *user, const char *reason) { struct gaim_conversation *conv; struct gaim_conversation_ui_ops *ops; char tmp[BUF_LONG]; GList *names; if (chat == NULL || user == NULL) return; conv = gaim_chat_get_conversation(chat); ops = gaim_conversation_get_ui_ops(conv); plugin_event(event_chat_buddy_leave, gaim_conversation_get_gc(conv), gaim_chat_get_id(chat), user); if (ops != NULL && ops->chat_remove_user != NULL) ops->chat_remove_user(conv, user); for (names = gaim_chat_get_users(chat); names != NULL; names = names->next) { if (!gaim_utf8_strcasecmp((char *)names->data, user)) { gaim_chat_set_users(chat, g_list_remove(gaim_chat_get_users(chat), names->data)); break; } } /* NOTE: Don't remove them from ignored in case they re-enter. */ if (chat_options & OPT_CHAT_LOGON) { if (reason != NULL && *reason != '\0') g_snprintf(tmp, sizeof(tmp), _("%s left the room (%s)."), user, reason); else g_snprintf(tmp, sizeof(tmp), _("%s left the room."), user); gaim_conversation_write(conv, NULL, tmp, -1, WFLAG_SYSTEM, time(NULL)); } } struct gaim_conversation * gaim_find_chat(struct gaim_connection *gc, int id) { GList *l; struct gaim_conversation *conv; for (l = gaim_get_chats(); l != NULL; l = l->next) { conv = (struct gaim_conversation *)l->data; if (gaim_chat_get_id(GAIM_CHAT(conv)) == id && gaim_conversation_get_gc(conv) == gc) return conv; } return NULL; } /************************************************************************** * Conversation placement functions **************************************************************************/ /* This one places conversations in the last made window. */ static void conv_placement_last_created_win(struct gaim_conversation *conv) { struct gaim_window *win; if (convo_options & OPT_CONVO_COMBINE) win = g_list_last(gaim_get_windows())->data; else win = gaim_get_last_window_with_type(gaim_conversation_get_type(conv)); if (win == NULL) { win = gaim_window_new(); gaim_window_add_conversation(win, conv); gaim_window_show(win); } else gaim_window_add_conversation(win, conv); } /* This one places each conversation in its own window. */ static void conv_placement_new_window(struct gaim_conversation *conv) { struct gaim_window *win; win = gaim_window_new(); gaim_window_add_conversation(win, conv); gaim_window_show(win); } /* * This groups things by, well, group. Buddies from groups will always be * grouped together, and a buddy from a group not belonging to any currently * open windows will get a new window. */ static void conv_placement_by_group(struct gaim_conversation *conv) { struct gaim_window *win; GaimConversationType type; type = gaim_conversation_get_type(conv); if (type != GAIM_CONV_IM) { win = gaim_get_last_window_with_type(type); if (win == NULL) conv_placement_new_window(conv); else gaim_window_add_conversation(win, conv); } else { struct buddy *b; struct group *grp = NULL; GList *wins, *convs; b = gaim_find_buddy(gaim_conversation_get_account(conv), gaim_conversation_get_name(conv)); if (b != NULL) grp = gaim_find_buddys_group(b); /* Go through the list of IMs and find one with this group. */ for (wins = gaim_get_windows(); wins != NULL; wins = wins->next) { struct gaim_window *win2; struct gaim_conversation *conv2; struct buddy *b2; struct group *g2 = NULL; win2 = (struct gaim_window *)wins->data; for (convs = gaim_window_get_conversations(win2); convs != NULL; convs = convs->next) { conv2 = (struct gaim_conversation *)convs->data; b2 = gaim_find_buddy(gaim_conversation_get_account(conv2), gaim_conversation_get_name(conv2)); if (b2 != NULL) g2 = gaim_find_buddys_group(b2); if (grp == g2) { gaim_window_add_conversation(win2, conv); return; } } } /* Make a new window. */ conv_placement_new_window(conv); } } /* This groups things by account. Otherwise, the same semantics as above */ static void conv_placement_by_account(struct gaim_conversation *conv) { GaimConversationType type; GList *wins, *convs; struct gaim_account *account; account = gaim_conversation_get_account(conv); type = gaim_conversation_get_type(conv); /* Go through the list of IMs and find one with this group. */ for (wins = gaim_get_windows(); wins != NULL; wins = wins->next) { struct gaim_window *win2; struct gaim_conversation *conv2; win2 = (struct gaim_window *)wins->data; for (convs = gaim_window_get_conversations(win2); convs != NULL; convs = convs->next) { conv2 = (struct gaim_conversation *)convs->data; if (((convo_options & OPT_CONVO_COMBINE) || type == gaim_conversation_get_type(conv2)) && account == gaim_conversation_get_account(conv2)) { gaim_window_add_conversation(win2, conv); return; } } } /* Make a new window. */ conv_placement_new_window(conv); } static int add_conv_placement_fnc(const char *name, gaim_conv_placement_fnc fnc) { struct ConvPlacementData *data; data = g_malloc0(sizeof(struct ConvPlacementData)); data->name = g_strdup(name); data->fnc = fnc; conv_placement_fncs = g_list_append(conv_placement_fncs, data); return gaim_conv_placement_get_fnc_count() - 1; } static void ensure_default_funcs(void) { if (conv_placement_fncs == NULL) { add_conv_placement_fnc(_("Last created window"), conv_placement_last_created_win); add_conv_placement_fnc(_("New window"), conv_placement_new_window); add_conv_placement_fnc(_("By group"), conv_placement_by_group); add_conv_placement_fnc(_("By account"), conv_placement_by_account); } } int gaim_conv_placement_add_fnc(const char *name, gaim_conv_placement_fnc fnc) { if (name == NULL || fnc == NULL) return -1; if (conv_placement_fncs == NULL) ensure_default_funcs(); return add_conv_placement_fnc(name, fnc); } void gaim_conv_placement_remove_fnc(int index) { struct ConvPlacementData *data; GList *node; if (index < 0 || index > g_list_length(conv_placement_fncs)) return; node = g_list_nth(conv_placement_fncs, index); data = (struct ConvPlacementData *)node->data; g_free(data->name); g_free(data); conv_placement_fncs = g_list_remove_link(conv_placement_fncs, node); g_list_free_1(node); } int gaim_conv_placement_get_fnc_count(void) { ensure_default_funcs(); return g_list_length(conv_placement_fncs); } const char * gaim_conv_placement_get_name(int index) { struct ConvPlacementData *data; ensure_default_funcs(); if (index < 0 || index > g_list_length(conv_placement_fncs)) return NULL; data = g_list_nth_data(conv_placement_fncs, index); if (data == NULL) return NULL; return data->name; } gaim_conv_placement_fnc gaim_conv_placement_get_fnc(int index) { struct ConvPlacementData *data; ensure_default_funcs(); if (index < 0 || index > g_list_length(conv_placement_fncs)) return NULL; data = g_list_nth_data(conv_placement_fncs, index); if (data == NULL) return NULL; return data->fnc; } int gaim_conv_placement_get_fnc_index(gaim_conv_placement_fnc fnc) { struct ConvPlacementData *data; GList *node; int i; ensure_default_funcs(); for (node = conv_placement_fncs, i = 0; node != NULL; node = node->next, i++) { data = (struct ConvPlacementData *)node->data; if (data->fnc == fnc) return i; } return -1; } int gaim_conv_placement_get_active(void) { return place_conv_index; } void gaim_conv_placement_set_active(int index) { gaim_conv_placement_fnc fnc; ensure_default_funcs(); fnc = gaim_conv_placement_get_fnc(index); if (fnc == NULL) return; place_conv = fnc; place_conv_index = index; } void gaim_set_win_ui_ops(struct gaim_window_ui_ops *ops) { win_ui_ops = ops; } struct gaim_window_ui_ops * gaim_get_win_ui_ops(void) { return win_ui_ops; }