[gaim-migrate @ 5229]
[pidgin-git.git] / src / conversation.c
blob308fcbae540f8ffc6552f874b280822f3ecd4283
1 /*
2 * gaim
4 * Copyright (C) 2002-2003, Christian Hammond <chipx86@gnupdate.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <string.h>
27 #include <stdlib.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include "conversation.h"
32 #include "gaim.h"
33 #include "prpl.h"
35 #ifdef _WIN32
36 #include "win32dep.h"
37 #endif
39 struct ConvPlacementData
41 char *name;
42 gaim_conv_placement_fnc fnc;
45 #define SEND_TYPED_TIMEOUT 5000
47 static struct gaim_window_ui_ops *win_ui_ops = NULL;
49 static GList *conversations = NULL;
50 static GList *ims = NULL;
51 static GList *chats = NULL;
52 static GList *windows = NULL;
53 static GList *conv_placement_fncs = NULL;
54 static gaim_conv_placement_fnc place_conv = NULL;
55 static int place_conv_index = -1;
57 static gint
58 insertname_compare(gconstpointer one, gconstpointer two)
60 const char *a = (const char *)one;
61 const char *b = (const char *)two;
63 if (*a == '@') {
64 if (*b != '@') return -1;
66 return strcasecmp(a + 1, b + 1);
68 } else if (*a == '%') {
69 if (*b != '%') return -1;
71 return strcasecmp(a + 1, b + 1);
73 } else if (*a == '+') {
74 if (*b == '@') return 1;
75 if (*b != '+') return -1;
77 return strcasecmp(a + 1, b + 1);
79 } else if (*b == '@' || *b == '%' || *b == '+')
80 return 1;
82 return strcasecmp(a, b);
85 static gboolean
86 find_nick(struct gaim_connection *gc, const char *message)
88 char *msg, *who, *p;
89 int n;
91 msg = g_utf8_strdown(message, -1);
93 who = g_utf8_strdown(gc->username, -1);
94 n = strlen(who);
96 if ((p = strstr(msg, who)) != NULL) {
97 if ((p == msg || !isalnum(*(p - 1))) && !isalnum(*(p + n))) {
98 g_free(who);
99 g_free(msg);
101 return TRUE;
105 g_free(who);
107 if (!gaim_utf8_strcasecmp(gc->username, gc->displayname)) {
108 g_free(msg);
110 return FALSE;
113 who = g_utf8_strdown(gc->displayname, -1);
114 n = strlen(who);
116 if (n > 0 && (p = strstr(msg, who)) != NULL) {
117 if ((p == msg || !isalnum(*(p - 1))) && !isalnum(*(p + n))) {
118 g_free(who);
119 g_free(msg);
121 return TRUE;
125 g_free(who);
126 g_free(msg);
128 return FALSE;
131 static gboolean
132 reset_typing(gpointer data)
134 char *name = (char *)data;
135 struct gaim_conversation *c = gaim_find_conversation(name);
136 struct gaim_im *im;
138 if (!c)
139 return FALSE;
141 im = GAIM_IM(c);
143 gaim_im_set_typing_state(im, NOT_TYPING);
144 gaim_im_update_typing(im);
145 gaim_im_stop_typing_timeout(im);
147 return FALSE;
150 static gboolean
151 send_typed(gpointer data)
153 struct gaim_conversation *conv = (struct gaim_conversation *)data;
154 struct gaim_connection *gc;
155 const char *name;
157 gc = gaim_conversation_get_gc(conv);
158 name = gaim_conversation_get_name(conv);
160 if (conv != NULL && gc != NULL && name != NULL) {
161 gaim_im_set_type_again(GAIM_IM(conv), TRUE);
163 /* XXX Somebody add const stuff! */
164 serv_send_typing(gc, (char *)name, TYPED);
166 debug_printf("typed...\n");
169 return FALSE;
172 static void
173 common_send(struct gaim_conversation *conv, const char *message)
175 GaimConversationType type;
176 struct gaim_connection *gc;
177 struct gaim_conversation_ui_ops *ops;
178 char *buf, *buf2, *buffy;
179 gulong length = 0;
180 gboolean binary = FALSE;
181 int plugin_return;
182 int limit;
183 int err = 0;
184 GList *first;
186 if ((gc = gaim_conversation_get_gc(conv)) == NULL)
187 return;
189 type = gaim_conversation_get_type(conv);
190 ops = gaim_conversation_get_ui_ops(conv);
192 limit = 32 * 1024; /* You shouldn't be sending more than 32K in your
193 messages. That's a book. */
195 buf = g_malloc(limit);
196 strncpy(buf, message, limit);
198 if (strlen(buf) == 0) {
199 g_free(buf);
201 return;
204 first = g_list_first(conv->send_history);
206 if (first->data)
207 g_free(first->data);
209 first->data = g_strdup(buf);
211 conv->send_history = g_list_prepend(first, NULL);
213 buf2 = g_malloc(limit);
215 if (gc->flags & OPT_CONN_HTML) {
216 if (convo_options & OPT_CONVO_SEND_LINKS)
217 linkify_text(buf);
220 buffy = g_strdup(buf);
221 plugin_return = plugin_event(
222 (type == GAIM_CONV_IM ? event_im_send : event_chat_send),
224 (type == GAIM_CONV_IM
225 ? gaim_conversation_get_name(conv)
226 : (void *)gaim_chat_get_id(GAIM_CHAT(conv))),
227 &buffy);
229 if (buffy == NULL) {
230 g_free(buf2);
231 g_free(buf);
232 return;
235 if (plugin_return) {
236 g_free(buffy);
237 g_free(buf2);
238 g_free(buf);
239 return;
242 strncpy(buf, buffy, limit);
243 g_free(buffy);
245 if (type == GAIM_CONV_IM) {
246 struct gaim_im *im = GAIM_IM(conv);
248 buffy = g_strdup(buf);
249 plugin_event(event_im_displayed_sent, gc,
250 gaim_conversation_get_name(conv), &buffy);
252 if (buffy != NULL) {
253 int imflags = 0;
255 if (conv->u.im->images != NULL) {
256 int id = 0, offset = 0;
257 char *bigbuf = NULL;
258 GSList *tmplist;
260 for (tmplist = conv->u.im->images;
261 tmplist != NULL;
262 tmplist = tmplist->next) {
264 char *img_filename = (char *)tmplist->data;
265 FILE *imgfile;
266 char *filename, *c;
267 struct stat st;
268 char imgtag[1024];
270 id++;
272 if (stat(img_filename, &st) != 0) {
273 debug_printf("Could not stat %s\n",
274 (char *)img_filename);
275 continue;
279 * Here we check to make sure the user still wants to send
280 * the image. He may have deleted the <img> tag in which
281 * case we don't want to send the binary data.
283 filename = img_filename;
285 while ((c = strchr(filename, '/')) != NULL)
286 filename = c + 1;
288 g_snprintf(imgtag, sizeof(imgtag),
289 "<IMG SRC=\"file://%s\" ID=\"%d\" "
290 "DATASIZE=\"%d\">",
291 filename, id, (int)st.st_size);
293 if (strstr(buffy, imgtag) == 0) {
294 debug_printf("Not sending image: %s\n", img_filename);
295 continue;
298 if (!binary) {
299 length = strlen(buffy) + strlen("<BINARY></BINARY>");
300 bigbuf = g_malloc(length + 1);
301 g_snprintf(bigbuf,
302 strlen(buffy) + strlen("<BINARY> ") + 1,
303 "%s<BINARY>", buffy);
305 offset = strlen(buffy) + strlen("<BINARY>");
306 binary = TRUE;
309 g_snprintf(imgtag, sizeof(imgtag),
310 "<DATA ID=\"%d\" SIZE=\"%d\">",
311 id, (int)st.st_size);
313 length += strlen(imgtag) + st.st_size + strlen("</DATA>");
315 bigbuf = g_realloc(bigbuf, length + 1);
317 if ((imgfile = fopen(img_filename, "r")) == NULL) {
318 debug_printf("Could not open %s\n", img_filename);
319 continue;
322 strncpy(bigbuf + offset, imgtag, strlen(imgtag) + 1);
324 offset += strlen(imgtag);
325 offset += fread(bigbuf + offset, 1, st.st_size, imgfile);
327 fclose(imgfile);
329 strncpy(bigbuf + offset, "</DATA>",
330 strlen("</DATA>") + 1);
332 offset += strlen("</DATA>");
335 if (binary) {
336 strncpy(bigbuf + offset, "</BINARY>",
337 strlen("<BINARY>") + 1);
339 err = serv_send_im(gc,
340 (char *)gaim_conversation_get_name(conv),
341 bigbuf, length, imflags);
343 else
344 err = serv_send_im(gc,
345 (char *)gaim_conversation_get_name(conv),
346 buffy, -1, imflags);
348 if (err > 0) {
349 GSList *tempy;
351 for (tempy = conv->u.im->images;
352 tempy != NULL;
353 tempy = tempy->next) {
355 g_free(tempy->data);
358 g_slist_free(conv->u.im->images);
359 conv->u.im->images = NULL;
361 if (binary)
362 gaim_im_write(im, NULL, bigbuf, length,
363 WFLAG_SEND, time(NULL));
364 else
365 gaim_im_write(im, NULL, buffy, -1, WFLAG_SEND,
366 time(NULL));
368 if (im_options & OPT_IM_POPDOWN)
369 gaim_window_hide(gaim_conversation_get_window(conv));
372 if (binary)
373 g_free(bigbuf);
375 else {
376 err = serv_send_im(gc, (char *)gaim_conversation_get_name(conv),
377 buffy, -1, imflags);
379 if (err > 0) {
380 gaim_im_write(im, NULL, buf, -1, WFLAG_SEND, time(NULL));
382 if (im_options & OPT_IM_POPDOWN)
383 gaim_window_hide(gaim_conversation_get_window(conv));
387 g_free(buffy);
390 else {
391 err = serv_chat_send(gc, gaim_chat_get_id(GAIM_CHAT(conv)), buf);
394 g_free(buf2);
395 g_free(buf);
397 if (err < 0) {
398 if (err == -E2BIG)
399 do_error_dialog(_("Unable to send message. "
400 "The message is too large."), NULL,
401 GAIM_ERROR);
402 else if (err == -ENOTCONN)
403 debug_printf("Not yet connected.\n");
404 else
405 do_error_dialog(_("Unable to send message."), NULL, GAIM_ERROR);
407 else {
408 if (err > 0 && (away_options & OPT_AWAY_BACK_ON_IM)) {
409 if (awaymessage != NULL) {
410 do_im_back();
412 else if (gc->away) {
413 serv_set_away(gc, GAIM_AWAY_CUSTOM, NULL);
420 static void
421 update_conv_indexes(struct gaim_window *win)
423 GList *l;
424 int i;
426 for (l = gaim_window_get_conversations(win), i = 0;
427 l != NULL;
428 l = l->next, i++) {
430 struct gaim_conversation *conv = (struct gaim_conversation *)l->data;
432 conv->conversation_pos = i;
436 struct gaim_window *
437 gaim_window_new(void)
439 struct gaim_window *win;
441 win = g_malloc0(sizeof(struct gaim_window));
443 win->ui_ops = gaim_get_win_ui_ops();
445 if (win->ui_ops != NULL && win->ui_ops->new_window != NULL)
446 win->ui_ops->new_window(win);
448 windows = g_list_append(windows, win);
450 return win;
453 void
454 gaim_window_destroy(struct gaim_window *win)
456 struct gaim_window_ui_ops *ops;
457 GList *node;
459 if (win == NULL)
460 return;
462 ops = gaim_window_get_ui_ops(win);
465 * If there are any conversations in this, destroy them all. The last
466 * conversation will call gaim_window_destroy(), but this time, this
467 * check will fail and the window will actually be destroyed.
469 * This is needed because chats may not close right away. They may
470 * wait for notification first. When they get that, the window is
471 * already destroyed, and gaim either crashes or spits out gtk warnings.
472 * The problem is fixed with this check.
474 if (gaim_window_get_conversation_count(win) > 0) {
476 node = g_list_first(gaim_window_get_conversations(win));
477 while(node != NULL)
479 struct gaim_conversation *conv = node->data;
481 node = g_list_next(node);
483 gaim_conversation_destroy(conv);
486 else
488 if (ops != NULL && ops->destroy_window != NULL)
489 ops->destroy_window(win);
491 g_list_free(gaim_window_get_conversations(win));
493 windows = g_list_remove(windows, win);
495 g_free(win);
499 void
500 gaim_window_show(struct gaim_window *win)
502 struct gaim_window_ui_ops *ops;
504 if (win == NULL)
505 return;
507 ops = gaim_window_get_ui_ops(win);
509 if (ops == NULL || ops->show == NULL)
510 return;
512 ops->show(win);
515 void
516 gaim_window_hide(struct gaim_window *win)
518 struct gaim_window_ui_ops *ops;
520 if (win == NULL)
521 return;
523 ops = gaim_window_get_ui_ops(win);
525 if (ops == NULL || ops->hide == NULL)
526 return;
528 ops->hide(win);
531 void
532 gaim_window_raise(struct gaim_window *win)
534 struct gaim_window_ui_ops *ops;
536 if (win == NULL)
537 return;
539 ops = gaim_window_get_ui_ops(win);
541 if (ops == NULL || ops->raise == NULL)
542 return;
544 ops->raise(win);
547 void
548 gaim_window_flash(struct gaim_window *win)
550 struct gaim_window_ui_ops *ops;
552 if (win == NULL)
553 return;
555 ops = gaim_window_get_ui_ops(win);
557 if (ops == NULL || ops->flash == NULL)
558 return;
560 ops->flash(win);
563 void
564 gaim_window_set_ui_ops(struct gaim_window *win, struct gaim_window_ui_ops *ops)
566 struct gaim_conversation_ui_ops *conv_ops = NULL;
567 GList *l;
569 if (win == NULL || win->ui_ops == ops)
570 return;
572 if (ops != NULL) {
573 if (ops->get_conversation_ui_ops != NULL)
574 conv_ops = ops->get_conversation_ui_ops();
577 if (win->ui_ops != NULL) {
578 if (win->ui_ops->destroy_window != NULL)
579 win->ui_ops->destroy_window(win);
582 win->ui_ops = ops;
584 if (win->ui_ops != NULL) {
585 if (win->ui_ops->new_window != NULL)
586 win->ui_ops->new_window(win);
589 for (l = gaim_window_get_conversations(win);
590 l != NULL;
591 l = l->next) {
593 struct gaim_conversation *conv = (struct gaim_conversation *)l;
595 gaim_conversation_set_ui_ops(conv, conv_ops);
597 if (win->ui_ops != NULL && win->ui_ops->add_conversation != NULL)
598 win->ui_ops->add_conversation(win, conv);
602 struct gaim_window_ui_ops *
603 gaim_window_get_ui_ops(const struct gaim_window *win)
605 if (win == NULL)
606 return NULL;
608 return win->ui_ops;
612 gaim_window_add_conversation(struct gaim_window *win,
613 struct gaim_conversation *conv)
615 struct gaim_window_ui_ops *ops;
617 if (win == NULL || conv == NULL)
618 return -1;
620 if (gaim_conversation_get_window(conv) != NULL) {
621 gaim_window_remove_conversation(
622 gaim_conversation_get_window(conv),
623 gaim_conversation_get_index(conv));
626 ops = gaim_window_get_ui_ops(win);
628 win->conversations = g_list_append(win->conversations, conv);
629 win->conversation_count++;
631 conv->conversation_pos = win->conversation_count - 1;
633 if (ops != NULL) {
634 conv->window = win;
636 if (ops->get_conversation_ui_ops != NULL)
637 gaim_conversation_set_ui_ops(conv, ops->get_conversation_ui_ops());
639 if (ops->add_conversation != NULL)
640 ops->add_conversation(win, conv);
643 return win->conversation_count - 1;
646 struct gaim_conversation *
647 gaim_window_remove_conversation(struct gaim_window *win, unsigned int index)
649 struct gaim_window_ui_ops *ops;
650 struct gaim_conversation *conv;
651 GList *node;
653 if (win == NULL || index >= gaim_window_get_conversation_count(win))
654 return NULL;
656 ops = gaim_window_get_ui_ops(win);
658 node = g_list_nth(gaim_window_get_conversations(win), index);
659 conv = (struct gaim_conversation *)node->data;
661 if (ops != NULL && ops->remove_conversation != NULL)
662 ops->remove_conversation(win, conv);
664 win->conversations = g_list_remove_link(win->conversations, node);
666 g_list_free_1(node);
668 win->conversation_count--;
670 conv->window = NULL;
672 if (gaim_window_get_conversation_count(win) == 0)
673 gaim_window_destroy(win);
674 else {
675 /* Change all the indexes. */
676 update_conv_indexes(win);
679 return conv;
682 void
683 gaim_window_move_conversation(struct gaim_window *win, unsigned int index,
684 unsigned int new_index)
686 struct gaim_window_ui_ops *ops;
687 struct gaim_conversation *conv;
688 GList *l;
690 if (win == NULL || index >= gaim_window_get_conversation_count(win) ||
691 index == new_index)
692 return;
694 /* We can't move this past the last index. */
695 if (new_index > gaim_window_get_conversation_count(win))
696 new_index = gaim_window_get_conversation_count(win);
698 /* Get the list item for this conversation at its current index. */
699 l = g_list_nth(gaim_window_get_conversations(win), index);
701 if (l == NULL) {
702 /* Should never happen. */
703 debug_printf("Misordered conversations list in window %p\n", win);
705 return;
708 conv = (struct gaim_conversation *)l->data;
710 /* Update the UI part of this. */
711 ops = gaim_window_get_ui_ops(win);
713 if (ops != NULL && ops->move_conversation != NULL)
714 ops->move_conversation(win, conv, new_index);
716 if (new_index > index)
717 new_index--;
719 /* Remove the old one. */
720 win->conversations = g_list_delete_link(win->conversations, l);
722 /* Insert it where it should go. */
723 win->conversations = g_list_insert(win->conversations, conv, new_index);
725 update_conv_indexes(win);
728 struct gaim_conversation *
729 gaim_window_get_conversation_at(const struct gaim_window *win,
730 unsigned int index)
732 if (win == NULL || index >= gaim_window_get_conversation_count(win))
733 return NULL;
735 return (struct gaim_conversation *)g_list_nth_data(
736 gaim_window_get_conversations(win), index);
739 size_t
740 gaim_window_get_conversation_count(const struct gaim_window *win)
742 if (win == NULL)
743 return 0;
745 return win->conversation_count;
748 void
749 gaim_window_switch_conversation(struct gaim_window *win, unsigned int index)
751 struct gaim_window_ui_ops *ops;
753 if (win == NULL || index < 0 ||
754 index >= gaim_window_get_conversation_count(win))
755 return;
757 ops = gaim_window_get_ui_ops(win);
759 if (ops != NULL && ops->switch_conversation != NULL)
760 ops->switch_conversation(win, index);
762 gaim_conversation_set_unseen(
763 gaim_window_get_conversation_at(win, index), 0);
766 struct gaim_conversation *
767 gaim_window_get_active_conversation(const struct gaim_window *win)
769 struct gaim_window_ui_ops *ops;
771 if (win == NULL)
772 return NULL;
774 ops = gaim_window_get_ui_ops(win);
776 if (ops != NULL && ops->get_active_index != NULL)
777 return gaim_window_get_conversation_at(win, ops->get_active_index(win));
779 return NULL;
782 GList *
783 gaim_window_get_conversations(const struct gaim_window *win)
785 if (win == NULL)
786 return NULL;
788 return win->conversations;
791 GList *
792 gaim_get_windows(void)
794 return windows;
797 struct gaim_window *
798 gaim_get_first_window_with_type(GaimConversationType type)
800 GList *wins, *convs;
801 struct gaim_window *win;
802 struct gaim_conversation *conv;
804 if (type == GAIM_CONV_UNKNOWN)
805 return NULL;
807 for (wins = gaim_get_windows(); wins != NULL; wins = wins->next) {
808 win = (struct gaim_window *)wins->data;
810 for (convs = gaim_window_get_conversations(win);
811 convs != NULL;
812 convs = convs->next) {
814 conv = (struct gaim_conversation *)convs->data;
816 if (gaim_conversation_get_type(conv) == type)
817 return win;
821 return NULL;
824 struct gaim_window *
825 gaim_get_last_window_with_type(GaimConversationType type)
827 GList *wins, *convs;
828 struct gaim_window *win;
829 struct gaim_conversation *conv;
831 if (type == GAIM_CONV_UNKNOWN)
832 return NULL;
834 for (wins = g_list_last(gaim_get_windows());
835 wins != NULL;
836 wins = wins->prev) {
838 win = (struct gaim_window *)wins->data;
840 for (convs = gaim_window_get_conversations(win);
841 convs != NULL;
842 convs = convs->next) {
844 conv = (struct gaim_conversation *)convs->data;
846 if (gaim_conversation_get_type(conv) == type)
847 return win;
851 return NULL;
854 /**************************************************************************
855 * Conversation API
856 **************************************************************************/
857 struct gaim_conversation *
858 gaim_conversation_new(GaimConversationType type, struct gaim_account *account,
859 const char *name)
861 struct gaim_conversation *conv;
863 if (type == GAIM_CONV_UNKNOWN)
864 return NULL;
866 /* Check if this conversation already exists. */
867 if ((conv = gaim_find_conversation_with_account(name, account)) != NULL)
868 return conv;
870 conv = g_malloc0(sizeof(struct gaim_conversation));
872 conv->type = type;
873 conv->account = account;
874 conv->name = g_strdup(name);
875 conv->title = g_strdup(name);
876 conv->send_history = g_list_append(NULL, NULL);
877 conv->history = g_string_new("");
878 conv->data = g_hash_table_new_full(g_str_hash, g_str_equal,
879 g_free, NULL);
881 if (type == GAIM_CONV_IM)
883 conv->u.im = g_malloc0(sizeof(struct gaim_im));
884 conv->u.im->conv = conv;
886 ims = g_list_append(ims, conv);
888 gaim_conversation_set_logging(conv,
889 (logging_options & OPT_LOG_CONVOS));
891 else if (type == GAIM_CONV_CHAT)
893 conv->u.chat = g_malloc0(sizeof(struct gaim_chat));
894 conv->u.chat->conv = conv;
896 chats = g_list_append(chats, conv);
898 gaim_conversation_set_logging(conv, (logging_options & OPT_LOG_CHATS));
901 conversations = g_list_append(conversations, conv);
903 /* Auto-set the title. */
904 gaim_conversation_autoset_title(conv);
907 * Create a window if one does not exist. If it does, use the last
908 * created window.
910 if (windows == NULL ||
911 (type == GAIM_CONV_IM && !(im_options & OPT_IM_ONE_WINDOW)) ||
912 (type == GAIM_CONV_CHAT && !(chat_options & OPT_CHAT_ONE_WINDOW))) {
913 struct gaim_window *win;
915 win = gaim_window_new();
916 gaim_window_add_conversation(win, conv);
918 /* Ensure the window is visible. */
919 gaim_window_show(win);
921 else {
922 if (place_conv == NULL)
923 gaim_conv_placement_set_active(0);
925 place_conv(conv);
928 plugin_event(event_new_conversation, name);
930 return conv;
933 void
934 gaim_conversation_destroy(struct gaim_conversation *conv)
936 struct gaim_window *win;
937 struct gaim_conversation_ui_ops *ops;
938 struct gaim_connection *gc;
939 const char *name;
940 GList *node;
942 if (conv == NULL)
943 return;
945 win = gaim_conversation_get_window(conv);
946 ops = gaim_conversation_get_ui_ops(conv);
947 gc = gaim_conversation_get_gc(conv);
948 name = gaim_conversation_get_name(conv);
950 if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) {
951 if (!(misc_options & OPT_MISC_STEALTH_TYPING))
952 serv_send_typing(gc, (char *)name, NOT_TYPING);
954 if (gc && gc->prpl->convo_closed != NULL)
955 gc->prpl->convo_closed(gc, (char *)name);
957 else if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT) {
959 * This is unfortunately necessary, because calling serv_chat_leave()
960 * calls this gaim_conversation_destroy(), which leads to two calls
961 * here.. We can't just return after this, because then it'll return
962 * on the next pass. So, since serv_got_chat_left(), which is
963 * eventually called from the prpl that serv_chat_leave() calls,
964 * removes this conversation from the gc's buddy_chats list, we're
965 * going to check to see if this exists in the list. If so, we want
966 * to return after calling this, because it'll be called again. If not,
967 * fall through, because it'll have already been removed, and we'd
968 * be on the 2nd pass.
970 * Long paragraph. <-- Short sentence.
972 * -- ChipX86
975 if (gc && g_slist_find(gc->buddy_chats, conv) != NULL) {
976 serv_chat_leave(gc, gaim_chat_get_id(GAIM_CHAT(conv)));
978 return;
982 plugin_event(event_del_conversation, conv);
984 if (conv->name != NULL) g_free(conv->name);
985 if (conv->title != NULL) g_free(conv->title);
987 for (node = g_list_first(conv->send_history);
988 node != NULL;
989 node = g_list_next(node)) {
991 if (node->data != NULL)
992 g_free(node->data);
995 g_list_free(g_list_first(conv->send_history));
997 if (conv->history != NULL)
998 g_string_free(conv->history, TRUE);
1000 conversations = g_list_remove(conversations, conv);
1002 if (conv->type == GAIM_CONV_IM) {
1003 GSList *snode;
1005 gaim_im_stop_typing_timeout(conv->u.im);
1006 gaim_im_stop_type_again_timeout(conv->u.im);
1008 for (snode = conv->u.im->images; snode != NULL; snode = snode->next) {
1009 if (snode->data != NULL)
1010 g_free(snode->data);
1013 g_slist_free(conv->u.im->images);
1015 g_free(conv->u.im);
1017 ims = g_list_remove(ims, conv);
1019 else if (conv->type == GAIM_CONV_CHAT) {
1021 for (node = conv->u.chat->in_room; node != NULL; node = node->next) {
1022 if (node->data != NULL)
1023 g_free(node->data);
1026 for (node = conv->u.chat->ignored; node != NULL; node = node->next) {
1027 if (node->data != NULL)
1028 g_free(node->data);
1031 g_list_free(conv->u.chat->in_room);
1032 g_list_free(conv->u.chat->ignored);
1034 if (conv->u.chat->who != NULL)
1035 g_free(conv->u.chat->who);
1037 if (conv->u.chat->topic != NULL)
1038 g_free(conv->u.chat->topic);
1040 g_free(conv->u.chat);
1042 chats = g_list_remove(chats, conv);
1045 g_hash_table_destroy(conv->data);
1047 if (win != NULL) {
1048 gaim_window_remove_conversation(win,
1049 gaim_conversation_get_index(conv));
1052 if (ops != NULL && ops->destroy_conversation != NULL)
1053 ops->destroy_conversation(conv);
1055 g_free(conv);
1058 GaimConversationType
1059 gaim_conversation_get_type(const struct gaim_conversation *conv)
1061 if (conv == NULL)
1062 return GAIM_CONV_UNKNOWN;
1064 return conv->type;
1067 void
1068 gaim_conversation_set_ui_ops(struct gaim_conversation *conv,
1069 struct gaim_conversation_ui_ops *ops)
1071 if (conv == NULL || conv->ui_ops == ops)
1072 return;
1074 if (conv->ui_ops != NULL && conv->ui_ops->destroy_conversation != NULL)
1075 conv->ui_ops->destroy_conversation(conv);
1077 conv->ui_data = NULL;
1079 conv->ui_ops = ops;
1082 struct gaim_conversation_ui_ops *
1083 gaim_conversation_get_ui_ops(struct gaim_conversation *conv)
1085 if (conv == NULL)
1086 return NULL;
1088 return conv->ui_ops;
1091 void
1092 gaim_conversation_set_account(struct gaim_conversation *conv,
1093 struct gaim_account *account)
1095 if (conv == NULL || account == gaim_conversation_get_account(conv))
1096 return;
1098 conv->account = account;
1100 gaim_conversation_update(conv, GAIM_CONV_UPDATE_ACCOUNT);
1103 struct gaim_account *
1104 gaim_conversation_get_account(const struct gaim_conversation *conv)
1106 if (conv == NULL)
1107 return NULL;
1109 return conv->account;
1112 struct gaim_connection *
1113 gaim_conversation_get_gc(const struct gaim_conversation *conv)
1115 struct gaim_account *account;
1117 if (conv == NULL)
1118 return NULL;
1120 account = gaim_conversation_get_account(conv);
1122 if (account == NULL)
1123 return NULL;
1125 return account->gc;
1128 void
1129 gaim_conversation_set_title(struct gaim_conversation *conv, const char *title)
1131 struct gaim_conversation_ui_ops *ops;
1133 if (conv == NULL || title == NULL)
1134 return;
1136 if (conv->title != NULL)
1137 g_free(conv->title);
1139 conv->title = g_strdup(title);
1141 ops = gaim_conversation_get_ui_ops(conv);
1143 if (ops != NULL && ops->set_title != NULL)
1144 ops->set_title(conv, conv->title);
1147 const char *
1148 gaim_conversation_get_title(const struct gaim_conversation *conv)
1150 if (conv == NULL)
1151 return NULL;
1153 return conv->title;
1156 void
1157 gaim_conversation_autoset_title(struct gaim_conversation *conv)
1159 struct gaim_account *account;
1160 struct buddy *b;
1161 const char *text, *name;
1163 if (conv == NULL)
1164 return;
1166 account = gaim_conversation_get_account(conv);
1167 name = gaim_conversation_get_name(conv);
1169 if (((im_options & OPT_IM_ALIAS_TAB) == OPT_IM_ALIAS_TAB) &&
1170 account != NULL && ((b = gaim_find_buddy(account, name)) != NULL)) {
1172 text = gaim_get_buddy_alias(b);
1174 else
1175 text = name;
1177 gaim_conversation_set_title(conv, text);
1181 gaim_conversation_get_index(const struct gaim_conversation *conv)
1183 if (conv == NULL)
1184 return 0;
1186 return conv->conversation_pos;
1189 void
1190 gaim_conversation_set_unseen(struct gaim_conversation *conv,
1191 GaimUnseenState state)
1193 if (conv == NULL)
1194 return;
1196 conv->unseen = state;
1198 gaim_conversation_update(conv, GAIM_CONV_UPDATE_UNSEEN);
1201 void
1202 gaim_conversation_foreach(void (*func)(struct gaim_conversation *conv))
1204 struct gaim_conversation *conv;
1205 GList *l;
1207 if (func == NULL)
1208 return;
1210 for (l = gaim_get_conversations(); l != NULL; l = l->next) {
1211 conv = (struct gaim_conversation *)l->data;
1213 func(conv);
1217 GaimUnseenState
1218 gaim_conversation_get_unseen(const struct gaim_conversation *conv)
1220 if (conv == NULL)
1221 return 0;
1223 return conv->unseen;
1226 const char *
1227 gaim_conversation_get_name(const struct gaim_conversation *conv)
1229 if (conv == NULL)
1230 return NULL;
1232 return conv->name;
1235 void
1236 gaim_conversation_set_logging(struct gaim_conversation *conv, gboolean log)
1238 if (conv == NULL)
1239 return;
1241 conv->logging = log;
1243 gaim_conversation_update(conv, GAIM_CONV_UPDATE_LOGGING);
1246 gboolean
1247 gaim_conversation_is_logging(const struct gaim_conversation *conv)
1249 if (conv == NULL)
1250 return FALSE;
1252 return conv->logging;
1255 GList *
1256 gaim_conversation_get_send_history(const struct gaim_conversation *conv)
1258 if (conv == NULL)
1259 return NULL;
1261 return conv->send_history;
1264 void
1265 gaim_conversation_set_history(struct gaim_conversation *conv,
1266 GString *history)
1268 if (conv == NULL)
1269 return;
1271 conv->history = history;
1274 GString *
1275 gaim_conversation_get_history(const struct gaim_conversation *conv)
1277 if (conv == NULL)
1278 return NULL;
1280 return conv->history;
1283 struct gaim_window *
1284 gaim_conversation_get_window(const struct gaim_conversation *conv)
1286 if (conv == NULL)
1287 return NULL;
1289 return conv->window;
1292 struct gaim_im *
1293 gaim_conversation_get_im_data(const struct gaim_conversation *conv)
1295 if (conv == NULL)
1296 return NULL;
1298 if (gaim_conversation_get_type(conv) != GAIM_CONV_IM)
1299 return NULL;
1301 return conv->u.im;
1304 struct gaim_chat *
1305 gaim_conversation_get_chat_data(const struct gaim_conversation *conv)
1307 if (conv == NULL)
1308 return NULL;
1310 if (gaim_conversation_get_type(conv) != GAIM_CONV_CHAT)
1311 return NULL;
1313 return conv->u.chat;
1316 void
1317 gaim_conversation_set_data(struct gaim_conversation *conv, const char *key,
1318 gpointer data)
1320 if (conv == NULL || key == NULL)
1321 return;
1323 g_hash_table_replace(conv->data, g_strdup(key), data);
1326 gpointer
1327 gaim_conversation_get_data(struct gaim_conversation *conv, const char *key)
1329 if (conv == NULL || key == NULL)
1330 return NULL;
1332 return g_hash_table_lookup(conv->data, key);
1335 GList *
1336 gaim_get_conversations(void)
1338 return conversations;
1341 GList *
1342 gaim_get_ims(void)
1344 return ims;
1347 GList *
1348 gaim_get_chats(void)
1350 return chats;
1353 struct gaim_conversation *
1354 gaim_find_conversation(const char *name)
1356 struct gaim_conversation *c = NULL;
1357 char *cuser;
1358 GList *cnv;
1360 if (name == NULL)
1361 return NULL;
1363 cuser = g_strdup(normalize(name));
1365 for (cnv = gaim_get_conversations(); cnv != NULL; cnv = cnv->next) {
1366 c = (struct gaim_conversation *)cnv->data;
1368 if (!gaim_utf8_strcasecmp(cuser, normalize(gaim_conversation_get_name(c))))
1369 break;
1371 c = NULL;
1374 g_free(cuser);
1376 return c;
1379 struct gaim_conversation *
1380 gaim_find_conversation_with_account(const char *name, const struct gaim_account *account)
1382 struct gaim_conversation *c = NULL;
1383 char *cuser;
1384 GList *cnv;
1386 if (name == NULL)
1387 return NULL;
1389 cuser = g_strdup(normalize(name));
1391 for (cnv = gaim_get_conversations(); cnv != NULL; cnv = cnv->next) {
1392 c = (struct gaim_conversation *)cnv->data;
1394 if (!gaim_utf8_strcasecmp(cuser, normalize(gaim_conversation_get_name(c))) &&
1395 account == gaim_conversation_get_account(c)) {
1397 break;
1400 c = NULL;
1403 g_free(cuser);
1405 return c;
1408 void
1409 gaim_conversation_write(struct gaim_conversation *conv, const char *who,
1410 const char *message, size_t length, int flags,
1411 time_t mtime)
1413 struct gaim_account *account;
1414 struct gaim_conversation_ui_ops *ops;
1415 struct gaim_window *win;
1416 struct buddy *b;
1417 GaimUnseenState unseen;
1418 /* int logging_font_options = 0; */
1420 if (conv == NULL || message == NULL)
1421 return;
1423 ops = gaim_conversation_get_ui_ops(conv);
1425 if (ops == NULL || ops->write_conv == NULL)
1426 return;
1428 account = gaim_conversation_get_account(conv);
1430 if (gaim_conversation_get_type(conv) == GAIM_CONV_CHAT &&
1431 (account->gc == NULL || !g_slist_find(account->gc->buddy_chats, conv)))
1432 return;
1434 if (gaim_conversation_get_type(conv) == GAIM_CONV_IM &&
1435 !g_list_find(gaim_get_conversations(), conv))
1436 return;
1438 if (gaim_conversation_get_type(conv) == GAIM_CONV_IM ||
1439 !(account->gc->prpl->options & OPT_PROTO_UNIQUE_CHATNAME)) {
1441 if (who == NULL) {
1442 if (flags & WFLAG_SEND) {
1443 b = gaim_find_buddy(account, account->gc->username);
1444 if (b != NULL && strcmp(b->name, gaim_get_buddy_alias(b)))
1445 who = gaim_get_buddy_alias(b);
1446 else if (*account->alias)
1447 who = account->alias;
1448 else if (*account->gc->displayname)
1449 who = account->gc->displayname;
1450 else
1451 who = account->gc->username;
1453 else {
1454 b = gaim_find_buddy(account, gaim_conversation_get_name(conv));
1456 if (b != NULL)
1457 who = gaim_get_buddy_alias(b);
1458 else
1459 who = gaim_conversation_get_name(conv);
1462 else {
1463 b = gaim_find_buddy(account, who);
1465 if (b != NULL)
1466 who = gaim_get_buddy_alias(b);
1470 ops->write_conv(conv, who, message, length, flags, mtime);
1472 win = gaim_conversation_get_window(conv);
1474 if (!(flags & WFLAG_NOLOG) &&
1475 ((gaim_conversation_get_type(conv) == GAIM_CONV_CHAT &&
1476 (chat_options & OPT_CHAT_POPUP)) ||
1477 (gaim_conversation_get_type(conv) == GAIM_CONV_IM &&
1478 (im_options & OPT_IM_POPUP)))) {
1480 gaim_window_show(win);
1483 /* Tab highlighting */
1484 if (!(flags & WFLAG_RECV) && !(flags & WFLAG_SYSTEM))
1485 return;
1487 if (gaim_conversation_get_type(conv) == GAIM_CONV_IM) {
1488 if ((flags & WFLAG_RECV) == WFLAG_RECV)
1489 gaim_im_set_typing_state(GAIM_IM(conv), NOT_TYPING);
1492 if (gaim_window_get_active_conversation(win) != conv) {
1493 if ((flags & WFLAG_NICK) == WFLAG_NICK ||
1494 gaim_conversation_get_unseen(conv) == GAIM_UNSEEN_NICK)
1495 unseen = GAIM_UNSEEN_NICK;
1496 else
1497 unseen = GAIM_UNSEEN_TEXT;
1499 else
1500 unseen = GAIM_UNSEEN_NONE;
1502 gaim_conversation_set_unseen(conv, unseen);
1505 void
1506 gaim_conversation_update_progress(struct gaim_conversation *conv,
1507 float percent)
1509 struct gaim_conversation_ui_ops *ops;
1511 if (conv == NULL)
1512 return;
1514 if (percent < 0)
1515 percent = 0;
1518 * NOTE: A percent >= 1 indicates that the progress bar should be
1519 * closed.
1521 ops = gaim_conversation_get_ui_ops(conv);
1523 if (ops != NULL && ops->update_progress != NULL)
1524 ops->update_progress(conv, percent);
1527 void
1528 gaim_conversation_update(struct gaim_conversation *conv,
1529 GaimConvUpdateType type)
1531 struct gaim_conversation_ui_ops *ops;
1533 if (conv == NULL)
1534 return;
1536 ops = gaim_conversation_get_ui_ops(conv);
1538 if (ops != NULL && ops->updated != NULL)
1539 ops->updated(conv, type);
1542 /**************************************************************************
1543 * IM Conversation API
1544 **************************************************************************/
1545 struct gaim_conversation *
1546 gaim_im_get_conversation(struct gaim_im *im)
1548 if (im == NULL)
1549 return NULL;
1551 return im->conv;
1554 void
1555 gaim_im_set_typing_state(struct gaim_im *im, int state)
1557 if (im == NULL)
1558 return;
1560 im->typing_state = state;
1564 gaim_im_get_typing_state(const struct gaim_im *im)
1566 if (im == NULL)
1567 return 0;
1569 return im->typing_state;
1572 void
1573 gaim_im_start_typing_timeout(struct gaim_im *im, int timeout)
1575 struct gaim_conversation *conv;
1576 const char *name;
1578 if (im == NULL)
1579 return;
1581 if (im->typing_timeout > 0)
1582 gaim_im_stop_typing_timeout(im);
1584 conv = gaim_im_get_conversation(im);
1585 name = gaim_conversation_get_name(conv);
1587 im->typing_timeout = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE,
1588 timeout * 1000, reset_typing, g_strdup(name), g_free);
1591 void
1592 gaim_im_stop_typing_timeout(struct gaim_im *im)
1594 if (im == NULL)
1595 return;
1597 if (im->typing_timeout == 0)
1598 return;
1600 g_source_remove(im->typing_timeout);
1601 im->typing_timeout = 0;
1604 guint
1605 gaim_im_get_typing_timeout(const struct gaim_im *im)
1607 if (im == NULL)
1608 return 0;
1610 return im->typing_timeout;
1613 void
1614 gaim_im_set_type_again(struct gaim_im *im, time_t val)
1616 if (im == NULL)
1617 return;
1619 im->type_again = val;
1622 time_t
1623 gaim_im_get_type_again(const struct gaim_im *im)
1625 if (im == NULL)
1626 return 0;
1628 return im->type_again;
1631 void
1632 gaim_im_start_type_again_timeout(struct gaim_im *im)
1634 if (im == NULL)
1635 return;
1637 im->type_again_timeout = g_timeout_add(SEND_TYPED_TIMEOUT, send_typed,
1638 gaim_im_get_conversation(im));
1641 void
1642 gaim_im_stop_type_again_timeout(struct gaim_im *im)
1644 if (im == NULL)
1645 return;
1647 if (im->type_again_timeout == 0)
1648 return;
1650 g_source_remove(im->type_again_timeout);
1651 im->type_again_timeout = 0;
1654 guint
1655 gaim_im_get_type_again_timeout(const struct gaim_im *im)
1657 if (im == NULL)
1658 return 0;
1660 return im->type_again_timeout;
1663 void
1664 gaim_im_update_typing(struct gaim_im *im)
1666 if (im == NULL)
1667 return;
1669 gaim_conversation_update(gaim_im_get_conversation(im),
1670 GAIM_CONV_UPDATE_TYPING);
1673 void
1674 gaim_im_write(struct gaim_im *im, const char *who, const char *message,
1675 size_t len, int flags, time_t mtime)
1677 struct gaim_conversation *c;
1679 if (im == NULL || message == NULL)
1680 return;
1682 c = gaim_im_get_conversation(im);
1684 /* Raise the window, if specified in prefs. */
1685 if (!(flags & WFLAG_NOLOG) & (im_options & OPT_IM_POPUP))
1686 gaim_window_raise(gaim_conversation_get_window(c));
1688 if (c->ui_ops != NULL && c->ui_ops->write_im != NULL)
1689 c->ui_ops->write_im(c, who, message, len, flags, mtime);
1690 else
1691 gaim_conversation_write(c, who, message, -1, flags, mtime);
1694 void
1695 gaim_im_send(struct gaim_im *im, const char *message)
1697 if (im == NULL || message == NULL)
1698 return;
1700 common_send(gaim_im_get_conversation(im), message);
1703 /**************************************************************************
1704 * Chat Conversation API
1705 **************************************************************************/
1707 struct gaim_conversation *
1708 gaim_chat_get_conversation(struct gaim_chat *chat)
1710 if (chat == NULL)
1711 return NULL;
1713 return chat->conv;
1716 GList *
1717 gaim_chat_set_users(struct gaim_chat *chat, GList *users)
1719 if (chat == NULL)
1720 return NULL;
1722 chat->in_room = users;
1724 return users;
1727 GList *
1728 gaim_chat_get_users(const struct gaim_chat *chat)
1730 if (chat == NULL)
1731 return NULL;
1733 return chat->in_room;
1736 void
1737 gaim_chat_ignore(struct gaim_chat *chat, const char *name)
1739 if (chat == NULL || name == NULL)
1740 return;
1742 /* Make sure the user isn't already ignored. */
1743 if (gaim_chat_is_user_ignored(chat, name))
1744 return;
1746 gaim_chat_set_ignored(chat,
1747 g_list_append(gaim_chat_get_ignored(chat), g_strdup(name)));
1750 void
1751 gaim_chat_unignore(struct gaim_chat *chat, const char *name)
1753 GList *item;
1755 if (chat == NULL || name == NULL)
1756 return;
1758 /* Make sure the user is actually ignored. */
1759 if (!gaim_chat_is_user_ignored(chat, name))
1760 return;
1762 item = g_list_find(gaim_chat_get_ignored(chat),
1763 gaim_chat_get_ignored_user(chat, name));
1765 gaim_chat_set_ignored(chat,
1766 g_list_remove_link(gaim_chat_get_ignored(chat), item));
1768 g_free(item->data);
1769 g_list_free_1(item);
1772 GList *
1773 gaim_chat_set_ignored(struct gaim_chat *chat, GList *ignored)
1775 if (chat == NULL)
1776 return NULL;
1778 chat->ignored = ignored;
1780 return ignored;
1783 GList *
1784 gaim_chat_get_ignored(const struct gaim_chat *chat)
1786 if (chat == NULL)
1787 return NULL;
1789 return chat->ignored;
1792 const char *
1793 gaim_chat_get_ignored_user(const struct gaim_chat *chat, const char *user)
1795 GList *ignored;
1797 if (chat == NULL || user == NULL)
1798 return NULL;
1800 for (ignored = gaim_chat_get_ignored(chat);
1801 ignored != NULL;
1802 ignored = ignored->next) {
1804 const char *ign = (const char *)ignored->data;
1806 if (!gaim_utf8_strcasecmp(user, ign) ||
1807 ((*ign == '+' || *ign == '%') && !gaim_utf8_strcasecmp(user, ign + 1)))
1808 return ign;
1810 if (*ign == '@') {
1811 ign++;
1813 if ((*ign == '+' && !gaim_utf8_strcasecmp(user, ign + 1)) ||
1814 (*ign != '+' && !gaim_utf8_strcasecmp(user, ign)))
1815 return ign;
1819 return NULL;
1822 gboolean
1823 gaim_chat_is_user_ignored(const struct gaim_chat *chat, const char *user)
1825 if (chat == NULL || user == NULL)
1826 return FALSE;
1828 return (gaim_chat_get_ignored_user(chat, user) != NULL);
1831 void
1832 gaim_chat_set_topic(struct gaim_chat *chat, const char *who, const char *topic)
1834 if (chat == NULL)
1835 return;
1837 if (chat->who != NULL) free(chat->who);
1838 if (chat->topic != NULL) free(chat->topic);
1840 chat->who = (who == NULL ? NULL : g_strdup(who));
1841 chat->topic = (topic == NULL ? NULL : g_strdup(topic));
1843 gaim_conversation_update(gaim_chat_get_conversation(chat),
1844 GAIM_CONV_UPDATE_TOPIC);
1847 const char *
1848 gaim_chat_get_topic(const struct gaim_chat *chat)
1850 if (chat == NULL)
1851 return NULL;
1853 return chat->topic;
1856 void
1857 gaim_chat_set_id(struct gaim_chat *chat, int id)
1859 if (chat == NULL)
1860 return;
1862 chat->id = id;
1866 gaim_chat_get_id(const struct gaim_chat *chat)
1868 if (chat == NULL)
1869 return -1;
1871 return chat->id;
1874 void
1875 gaim_chat_write(struct gaim_chat *chat, const char *who,
1876 const char *message, int flags, time_t mtime)
1878 struct gaim_conversation *conv;
1879 struct gaim_connection *gc;
1881 if (chat == NULL || who == NULL || message == NULL)
1882 return;
1884 conv = gaim_chat_get_conversation(chat);
1885 gc = gaim_conversation_get_gc(conv);
1887 /* Don't display this if the person who wrote it is ignored. */
1888 if (gaim_chat_is_user_ignored(chat, who))
1889 return;
1891 /* Raise the window, if specified in prefs. */
1892 if (!(flags & WFLAG_NOLOG) & (chat_options & OPT_CHAT_POPUP))
1893 gaim_window_raise(gaim_conversation_get_window(conv));
1895 if (!(flags & WFLAG_WHISPER)) {
1896 char *str;
1898 str = g_strdup(normalize(who));
1900 if (!gaim_utf8_strcasecmp(str, normalize(gc->username)) ||
1901 !gaim_utf8_strcasecmp(str, normalize(gc->displayname))) {
1903 flags |= WFLAG_SEND;
1905 else {
1906 flags |= WFLAG_RECV;
1908 if (find_nick(gc, message))
1909 flags |= WFLAG_NICK;
1912 g_free(str);
1915 /* Pass this on to either the ops structure or the default write func. */
1916 if (conv->ui_ops != NULL && conv->ui_ops->write_chat != NULL)
1917 conv->ui_ops->write_chat(conv, who, message, flags, mtime);
1918 else
1919 gaim_conversation_write(conv, who, message, -1, flags, mtime);
1922 void
1923 gaim_chat_send(struct gaim_chat *chat, const char *message)
1925 if (chat == NULL || message == NULL)
1926 return;
1928 common_send(gaim_chat_get_conversation(chat), message);
1931 void
1932 gaim_chat_add_user(struct gaim_chat *chat, const char *user,
1933 const char *extra_msg)
1935 struct gaim_conversation *conv;
1936 struct gaim_conversation_ui_ops *ops;
1937 char tmp[BUF_LONG];
1939 if (chat == NULL || user == NULL)
1940 return;
1942 conv = gaim_chat_get_conversation(chat);
1943 ops = gaim_conversation_get_ui_ops(conv);
1945 gaim_chat_set_users(chat,
1946 g_list_insert_sorted(gaim_chat_get_users(chat), g_strdup(user),
1947 insertname_compare));
1949 plugin_event(event_chat_buddy_join,
1950 gaim_conversation_get_gc(conv), gaim_chat_get_id(chat),
1951 user);
1953 if (ops != NULL && ops->chat_add_user != NULL)
1954 ops->chat_add_user(conv, user);
1956 if (chat_options & OPT_CHAT_LOGON) {
1957 if (extra_msg == NULL)
1958 g_snprintf(tmp, sizeof(tmp), _("%s entered the room."), user);
1959 else
1960 g_snprintf(tmp, sizeof(tmp),
1961 _("%s [<I>%s</I>] entered the room."),
1962 user, extra_msg);
1964 gaim_conversation_write(conv, NULL, tmp, -1, WFLAG_SYSTEM, time(NULL));
1968 void
1969 gaim_chat_rename_user(struct gaim_chat *chat, const char *old_user,
1970 const char *new_user)
1972 struct gaim_conversation *conv;
1973 struct gaim_conversation_ui_ops *ops;
1974 char tmp[BUF_LONG];
1975 GList *names;
1977 if (chat == NULL || old_user == NULL || new_user == NULL)
1978 return;
1980 conv = gaim_chat_get_conversation(chat);
1981 ops = gaim_conversation_get_ui_ops(conv);
1983 gaim_chat_set_users(chat,
1984 g_list_insert_sorted(gaim_chat_get_users(chat), g_strdup(new_user),
1985 insertname_compare));
1987 if (ops != NULL && ops->chat_rename_user != NULL)
1988 ops->chat_rename_user(conv, old_user, new_user);
1990 for (names = gaim_chat_get_users(chat);
1991 names != NULL;
1992 names = names->next) {
1994 if (!gaim_utf8_strcasecmp((char *)names->data, old_user)) {
1995 gaim_chat_set_users(chat,
1996 g_list_remove(gaim_chat_get_users(chat), names->data));
1997 break;
2001 if (gaim_chat_is_user_ignored(chat, old_user)) {
2002 gaim_chat_unignore(chat, old_user);
2003 gaim_chat_ignore(chat, new_user);
2005 else if (gaim_chat_is_user_ignored(chat, new_user))
2006 gaim_chat_unignore(chat, new_user);
2008 if (chat_options & OPT_CHAT_LOGON) {
2009 g_snprintf(tmp, sizeof(tmp),
2010 _("%s is now known as %s"), old_user, new_user);
2012 gaim_conversation_write(conv, NULL, tmp, -1, WFLAG_SYSTEM, time(NULL));
2016 void
2017 gaim_chat_remove_user(struct gaim_chat *chat, const char *user,
2018 const char *reason)
2020 struct gaim_conversation *conv;
2021 struct gaim_conversation_ui_ops *ops;
2022 char tmp[BUF_LONG];
2023 GList *names;
2025 if (chat == NULL || user == NULL)
2026 return;
2028 conv = gaim_chat_get_conversation(chat);
2029 ops = gaim_conversation_get_ui_ops(conv);
2031 plugin_event(event_chat_buddy_leave, gaim_conversation_get_gc(conv),
2032 gaim_chat_get_id(chat), user);
2034 if (ops != NULL && ops->chat_remove_user != NULL)
2035 ops->chat_remove_user(conv, user);
2037 for (names = gaim_chat_get_users(chat);
2038 names != NULL;
2039 names = names->next) {
2041 if (!gaim_utf8_strcasecmp((char *)names->data, user)) {
2042 gaim_chat_set_users(chat,
2043 g_list_remove(gaim_chat_get_users(chat), names->data));
2044 break;
2048 /* NOTE: Don't remove them from ignored in case they re-enter. */
2050 if (chat_options & OPT_CHAT_LOGON) {
2051 if (reason != NULL && *reason != '\0')
2052 g_snprintf(tmp, sizeof(tmp),
2053 _("%s left the room (%s)."), user, reason);
2054 else
2055 g_snprintf(tmp, sizeof(tmp), _("%s left the room."), user);
2057 gaim_conversation_write(conv, NULL, tmp, -1, WFLAG_SYSTEM, time(NULL));
2061 struct gaim_conversation *
2062 gaim_find_chat(struct gaim_connection *gc, int id)
2064 GList *l;
2065 struct gaim_conversation *conv;
2067 for (l = gaim_get_chats(); l != NULL; l = l->next) {
2068 conv = (struct gaim_conversation *)l->data;
2070 if (gaim_chat_get_id(GAIM_CHAT(conv)) == id &&
2071 gaim_conversation_get_gc(conv) == gc)
2072 return conv;
2075 return NULL;
2078 /**************************************************************************
2079 * Conversation placement functions
2080 **************************************************************************/
2081 /* This one places conversations in the last made window. */
2082 static void
2083 conv_placement_last_created_win(struct gaim_conversation *conv)
2085 struct gaim_window *win;
2087 if (convo_options & OPT_CONVO_COMBINE)
2088 win = g_list_last(gaim_get_windows())->data;
2089 else
2090 win = gaim_get_last_window_with_type(gaim_conversation_get_type(conv));
2092 if (win == NULL) {
2093 win = gaim_window_new();
2095 gaim_window_add_conversation(win, conv);
2096 gaim_window_show(win);
2098 else
2099 gaim_window_add_conversation(win, conv);
2102 /* This one places each conversation in its own window. */
2103 static void
2104 conv_placement_new_window(struct gaim_conversation *conv)
2106 struct gaim_window *win;
2108 win = gaim_window_new();
2110 gaim_window_add_conversation(win, conv);
2112 gaim_window_show(win);
2116 * This groups things by, well, group. Buddies from groups will always be
2117 * grouped together, and a buddy from a group not belonging to any currently
2118 * open windows will get a new window.
2120 static void
2121 conv_placement_by_group(struct gaim_conversation *conv)
2123 struct gaim_window *win;
2124 GaimConversationType type;
2126 type = gaim_conversation_get_type(conv);
2128 if (type != GAIM_CONV_IM) {
2129 win = gaim_get_last_window_with_type(type);
2131 if (win == NULL)
2132 conv_placement_new_window(conv);
2133 else
2134 gaim_window_add_conversation(win, conv);
2136 else {
2137 struct buddy *b;
2138 struct group *grp = NULL;
2139 GList *wins, *convs;
2141 b = gaim_find_buddy(gaim_conversation_get_account(conv),
2142 gaim_conversation_get_name(conv));
2144 if (b != NULL)
2145 grp = gaim_find_buddys_group(b);
2147 /* Go through the list of IMs and find one with this group. */
2148 for (wins = gaim_get_windows(); wins != NULL; wins = wins->next) {
2149 struct gaim_window *win2;
2150 struct gaim_conversation *conv2;
2151 struct buddy *b2;
2152 struct group *g2 = NULL;
2154 win2 = (struct gaim_window *)wins->data;
2156 for (convs = gaim_window_get_conversations(win2);
2157 convs != NULL;
2158 convs = convs->next) {
2160 conv2 = (struct gaim_conversation *)convs->data;
2162 b2 = gaim_find_buddy(gaim_conversation_get_account(conv2),
2163 gaim_conversation_get_name(conv2));
2165 if (b2 != NULL)
2166 g2 = gaim_find_buddys_group(b2);
2168 if (grp == g2) {
2169 gaim_window_add_conversation(win2, conv);
2171 return;
2176 /* Make a new window. */
2177 conv_placement_new_window(conv);
2181 /* This groups things by account. Otherwise, the same semantics as above */
2182 static void
2183 conv_placement_by_account(struct gaim_conversation *conv)
2185 GaimConversationType type;
2186 GList *wins, *convs;
2187 struct gaim_account *account;
2190 account = gaim_conversation_get_account(conv);
2191 type = gaim_conversation_get_type(conv);
2194 /* Go through the list of IMs and find one with this group. */
2195 for (wins = gaim_get_windows(); wins != NULL; wins = wins->next) {
2196 struct gaim_window *win2;
2197 struct gaim_conversation *conv2;
2199 win2 = (struct gaim_window *)wins->data;
2201 for (convs = gaim_window_get_conversations(win2);
2202 convs != NULL;
2203 convs = convs->next) {
2205 conv2 = (struct gaim_conversation *)convs->data;
2207 if (((convo_options & OPT_CONVO_COMBINE) ||
2208 type == gaim_conversation_get_type(conv2)) &&
2209 account == gaim_conversation_get_account(conv2)) {
2210 gaim_window_add_conversation(win2, conv);
2211 return;
2216 /* Make a new window. */
2217 conv_placement_new_window(conv);
2220 static int
2221 add_conv_placement_fnc(const char *name, gaim_conv_placement_fnc fnc)
2223 struct ConvPlacementData *data;
2225 data = g_malloc0(sizeof(struct ConvPlacementData));
2227 data->name = g_strdup(name);
2228 data->fnc = fnc;
2230 conv_placement_fncs = g_list_append(conv_placement_fncs, data);
2232 return gaim_conv_placement_get_fnc_count() - 1;
2235 static void
2236 ensure_default_funcs(void)
2238 if (conv_placement_fncs == NULL) {
2239 add_conv_placement_fnc(_("Last created window"),
2240 conv_placement_last_created_win);
2241 add_conv_placement_fnc(_("New window"),
2242 conv_placement_new_window);
2243 add_conv_placement_fnc(_("By group"),
2244 conv_placement_by_group);
2245 add_conv_placement_fnc(_("By account"),
2246 conv_placement_by_account);
2251 gaim_conv_placement_add_fnc(const char *name, gaim_conv_placement_fnc fnc)
2253 if (name == NULL || fnc == NULL)
2254 return -1;
2256 if (conv_placement_fncs == NULL)
2257 ensure_default_funcs();
2259 return add_conv_placement_fnc(name, fnc);
2262 void
2263 gaim_conv_placement_remove_fnc(int index)
2265 struct ConvPlacementData *data;
2266 GList *node;
2268 if (index < 0 || index > g_list_length(conv_placement_fncs))
2269 return;
2271 node = g_list_nth(conv_placement_fncs, index);
2272 data = (struct ConvPlacementData *)node->data;
2274 g_free(data->name);
2275 g_free(data);
2277 conv_placement_fncs = g_list_remove_link(conv_placement_fncs, node);
2278 g_list_free_1(node);
2282 gaim_conv_placement_get_fnc_count(void)
2284 ensure_default_funcs();
2286 return g_list_length(conv_placement_fncs);
2289 const char *
2290 gaim_conv_placement_get_name(int index)
2292 struct ConvPlacementData *data;
2294 ensure_default_funcs();
2296 if (index < 0 || index > g_list_length(conv_placement_fncs))
2297 return NULL;
2299 data = g_list_nth_data(conv_placement_fncs, index);
2301 if (data == NULL)
2302 return NULL;
2304 return data->name;
2307 gaim_conv_placement_fnc
2308 gaim_conv_placement_get_fnc(int index)
2310 struct ConvPlacementData *data;
2312 ensure_default_funcs();
2314 if (index < 0 || index > g_list_length(conv_placement_fncs))
2315 return NULL;
2317 data = g_list_nth_data(conv_placement_fncs, index);
2319 if (data == NULL)
2320 return NULL;
2322 return data->fnc;
2326 gaim_conv_placement_get_fnc_index(gaim_conv_placement_fnc fnc)
2328 struct ConvPlacementData *data;
2329 GList *node;
2330 int i;
2332 ensure_default_funcs();
2334 for (node = conv_placement_fncs, i = 0;
2335 node != NULL;
2336 node = node->next, i++) {
2338 data = (struct ConvPlacementData *)node->data;
2340 if (data->fnc == fnc)
2341 return i;
2344 return -1;
2348 gaim_conv_placement_get_active(void)
2350 return place_conv_index;
2353 void
2354 gaim_conv_placement_set_active(int index)
2356 gaim_conv_placement_fnc fnc;
2358 ensure_default_funcs();
2360 fnc = gaim_conv_placement_get_fnc(index);
2362 if (fnc == NULL)
2363 return;
2365 place_conv = fnc;
2366 place_conv_index = index;
2369 void
2370 gaim_set_win_ui_ops(struct gaim_window_ui_ops *ops)
2372 win_ui_ops = ops;
2375 struct gaim_window_ui_ops *
2376 gaim_get_win_ui_ops(void)
2378 return win_ui_ops;