4 * Copyright (C) 2002-2003, Christian Hammond <chipx86@gnupdate.org>
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
31 #include "conversation.h"
39 struct ConvPlacementData
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;
58 insertname_compare(gconstpointer one
, gconstpointer two
)
60 const char *a
= (const char *)one
;
61 const char *b
= (const char *)two
;
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
== '+')
82 return strcasecmp(a
, b
);
86 find_nick(struct gaim_connection
*gc
, const char *message
)
91 msg
= g_utf8_strdown(message
, -1);
93 who
= g_utf8_strdown(gc
->username
, -1);
96 if ((p
= strstr(msg
, who
)) != NULL
) {
97 if ((p
== msg
|| !isalnum(*(p
- 1))) && !isalnum(*(p
+ n
))) {
107 if (!gaim_utf8_strcasecmp(gc
->username
, gc
->displayname
)) {
113 who
= g_utf8_strdown(gc
->displayname
, -1);
116 if (n
> 0 && (p
= strstr(msg
, who
)) != NULL
) {
117 if ((p
== msg
|| !isalnum(*(p
- 1))) && !isalnum(*(p
+ n
))) {
132 reset_typing(gpointer data
)
134 char *name
= (char *)data
;
135 struct gaim_conversation
*c
= gaim_find_conversation(name
);
143 gaim_im_set_typing_state(im
, NOT_TYPING
);
144 gaim_im_update_typing(im
);
145 gaim_im_stop_typing_timeout(im
);
151 send_typed(gpointer data
)
153 struct gaim_conversation
*conv
= (struct gaim_conversation
*)data
;
154 struct gaim_connection
*gc
;
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");
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
;
180 gboolean binary
= FALSE
;
186 if ((gc
= gaim_conversation_get_gc(conv
)) == NULL
)
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) {
204 first
= g_list_first(conv
->send_history
);
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
)
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
))),
242 strncpy(buf
, buffy
, limit
);
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
);
255 if (conv
->u
.im
->images
!= NULL
) {
256 int id
= 0, offset
= 0;
260 for (tmplist
= conv
->u
.im
->images
;
262 tmplist
= tmplist
->next
) {
264 char *img_filename
= (char *)tmplist
->data
;
272 if (stat(img_filename
, &st
) != 0) {
273 debug_printf("Could not stat %s\n",
274 (char *)img_filename
);
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
)
288 g_snprintf(imgtag
, sizeof(imgtag
),
289 "<IMG SRC=\"file://%s\" ID=\"%d\" "
291 filename
, id
, (int)st
.st_size
);
293 if (strstr(buffy
, imgtag
) == 0) {
294 debug_printf("Not sending image: %s\n", img_filename
);
299 length
= strlen(buffy
) + strlen("<BINARY></BINARY>");
300 bigbuf
= g_malloc(length
+ 1);
302 strlen(buffy
) + strlen("<BINARY> ") + 1,
303 "%s<BINARY>", buffy
);
305 offset
= strlen(buffy
) + strlen("<BINARY>");
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
);
322 strncpy(bigbuf
+ offset
, imgtag
, strlen(imgtag
) + 1);
324 offset
+= strlen(imgtag
);
325 offset
+= fread(bigbuf
+ offset
, 1, st
.st_size
, imgfile
);
329 strncpy(bigbuf
+ offset
, "</DATA>",
330 strlen("</DATA>") + 1);
332 offset
+= strlen("</DATA>");
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
);
344 err
= serv_send_im(gc
,
345 (char *)gaim_conversation_get_name(conv
),
351 for (tempy
= conv
->u
.im
->images
;
353 tempy
= tempy
->next
) {
358 g_slist_free(conv
->u
.im
->images
);
359 conv
->u
.im
->images
= NULL
;
362 gaim_im_write(im
, NULL
, bigbuf
, length
,
363 WFLAG_SEND
, time(NULL
));
365 gaim_im_write(im
, NULL
, buffy
, -1, WFLAG_SEND
,
368 if (im_options
& OPT_IM_POPDOWN
)
369 gaim_window_hide(gaim_conversation_get_window(conv
));
376 err
= serv_send_im(gc
, (char *)gaim_conversation_get_name(conv
),
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
));
391 err
= serv_chat_send(gc
, gaim_chat_get_id(GAIM_CHAT(conv
)), buf
);
399 do_error_dialog(_("Unable to send message. "
400 "The message is too large."), NULL
,
402 else if (err
== -ENOTCONN
)
403 debug_printf("Not yet connected.\n");
405 do_error_dialog(_("Unable to send message."), NULL
, GAIM_ERROR
);
408 if (err
> 0 && (away_options
& OPT_AWAY_BACK_ON_IM
)) {
409 if (awaymessage
!= NULL
) {
413 serv_set_away(gc
, GAIM_AWAY_CUSTOM
, NULL
);
421 update_conv_indexes(struct gaim_window
*win
)
426 for (l
= gaim_window_get_conversations(win
), i
= 0;
430 struct gaim_conversation
*conv
= (struct gaim_conversation
*)l
->data
;
432 conv
->conversation_pos
= i
;
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
);
454 gaim_window_destroy(struct gaim_window
*win
)
456 struct gaim_window_ui_ops
*ops
;
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
));
479 struct gaim_conversation
*conv
= node
->data
;
481 node
= g_list_next(node
);
483 gaim_conversation_destroy(conv
);
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
);
500 gaim_window_show(struct gaim_window
*win
)
502 struct gaim_window_ui_ops
*ops
;
507 ops
= gaim_window_get_ui_ops(win
);
509 if (ops
== NULL
|| ops
->show
== NULL
)
516 gaim_window_hide(struct gaim_window
*win
)
518 struct gaim_window_ui_ops
*ops
;
523 ops
= gaim_window_get_ui_ops(win
);
525 if (ops
== NULL
|| ops
->hide
== NULL
)
532 gaim_window_raise(struct gaim_window
*win
)
534 struct gaim_window_ui_ops
*ops
;
539 ops
= gaim_window_get_ui_ops(win
);
541 if (ops
== NULL
|| ops
->raise
== NULL
)
548 gaim_window_flash(struct gaim_window
*win
)
550 struct gaim_window_ui_ops
*ops
;
555 ops
= gaim_window_get_ui_ops(win
);
557 if (ops
== NULL
|| ops
->flash
== NULL
)
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
;
569 if (win
== NULL
|| win
->ui_ops
== ops
)
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
);
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
);
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
)
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
)
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;
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
;
653 if (win
== NULL
|| index
>= gaim_window_get_conversation_count(win
))
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
);
668 win
->conversation_count
--;
672 if (gaim_window_get_conversation_count(win
) == 0)
673 gaim_window_destroy(win
);
675 /* Change all the indexes. */
676 update_conv_indexes(win
);
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
;
690 if (win
== NULL
|| index
>= gaim_window_get_conversation_count(win
) ||
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
);
702 /* Should never happen. */
703 debug_printf("Misordered conversations list in window %p\n", win
);
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
)
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
,
732 if (win
== NULL
|| index
>= gaim_window_get_conversation_count(win
))
735 return (struct gaim_conversation
*)g_list_nth_data(
736 gaim_window_get_conversations(win
), index
);
740 gaim_window_get_conversation_count(const struct gaim_window
*win
)
745 return win
->conversation_count
;
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
))
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
;
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
));
783 gaim_window_get_conversations(const struct gaim_window
*win
)
788 return win
->conversations
;
792 gaim_get_windows(void)
798 gaim_get_first_window_with_type(GaimConversationType type
)
801 struct gaim_window
*win
;
802 struct gaim_conversation
*conv
;
804 if (type
== GAIM_CONV_UNKNOWN
)
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
);
812 convs
= convs
->next
) {
814 conv
= (struct gaim_conversation
*)convs
->data
;
816 if (gaim_conversation_get_type(conv
) == type
)
825 gaim_get_last_window_with_type(GaimConversationType type
)
828 struct gaim_window
*win
;
829 struct gaim_conversation
*conv
;
831 if (type
== GAIM_CONV_UNKNOWN
)
834 for (wins
= g_list_last(gaim_get_windows());
838 win
= (struct gaim_window
*)wins
->data
;
840 for (convs
= gaim_window_get_conversations(win
);
842 convs
= convs
->next
) {
844 conv
= (struct gaim_conversation
*)convs
->data
;
846 if (gaim_conversation_get_type(conv
) == type
)
854 /**************************************************************************
856 **************************************************************************/
857 struct gaim_conversation
*
858 gaim_conversation_new(GaimConversationType type
, struct gaim_account
*account
,
861 struct gaim_conversation
*conv
;
863 if (type
== GAIM_CONV_UNKNOWN
)
866 /* Check if this conversation already exists. */
867 if ((conv
= gaim_find_conversation_with_account(name
, account
)) != NULL
)
870 conv
= g_malloc0(sizeof(struct gaim_conversation
));
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
,
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
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
);
922 if (place_conv
== NULL
)
923 gaim_conv_placement_set_active(0);
928 plugin_event(event_new_conversation
, name
);
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
;
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.
975 if (gc
&& g_slist_find(gc
->buddy_chats
, conv
) != NULL
) {
976 serv_chat_leave(gc
, gaim_chat_get_id(GAIM_CHAT(conv
)));
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
);
989 node
= g_list_next(node
)) {
991 if (node
->data
!= NULL
)
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
) {
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
);
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
)
1026 for (node
= conv
->u
.chat
->ignored
; node
!= NULL
; node
= node
->next
) {
1027 if (node
->data
!= NULL
)
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
);
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
);
1058 GaimConversationType
1059 gaim_conversation_get_type(const struct gaim_conversation
*conv
)
1062 return GAIM_CONV_UNKNOWN
;
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
)
1074 if (conv
->ui_ops
!= NULL
&& conv
->ui_ops
->destroy_conversation
!= NULL
)
1075 conv
->ui_ops
->destroy_conversation(conv
);
1077 conv
->ui_data
= NULL
;
1082 struct gaim_conversation_ui_ops
*
1083 gaim_conversation_get_ui_ops(struct gaim_conversation
*conv
)
1088 return conv
->ui_ops
;
1092 gaim_conversation_set_account(struct gaim_conversation
*conv
,
1093 struct gaim_account
*account
)
1095 if (conv
== NULL
|| account
== gaim_conversation_get_account(conv
))
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
)
1109 return conv
->account
;
1112 struct gaim_connection
*
1113 gaim_conversation_get_gc(const struct gaim_conversation
*conv
)
1115 struct gaim_account
*account
;
1120 account
= gaim_conversation_get_account(conv
);
1122 if (account
== NULL
)
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
)
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
);
1148 gaim_conversation_get_title(const struct gaim_conversation
*conv
)
1157 gaim_conversation_autoset_title(struct gaim_conversation
*conv
)
1159 struct gaim_account
*account
;
1161 const char *text
, *name
;
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
);
1177 gaim_conversation_set_title(conv
, text
);
1181 gaim_conversation_get_index(const struct gaim_conversation
*conv
)
1186 return conv
->conversation_pos
;
1190 gaim_conversation_set_unseen(struct gaim_conversation
*conv
,
1191 GaimUnseenState state
)
1196 conv
->unseen
= state
;
1198 gaim_conversation_update(conv
, GAIM_CONV_UPDATE_UNSEEN
);
1202 gaim_conversation_foreach(void (*func
)(struct gaim_conversation
*conv
))
1204 struct gaim_conversation
*conv
;
1210 for (l
= gaim_get_conversations(); l
!= NULL
; l
= l
->next
) {
1211 conv
= (struct gaim_conversation
*)l
->data
;
1218 gaim_conversation_get_unseen(const struct gaim_conversation
*conv
)
1223 return conv
->unseen
;
1227 gaim_conversation_get_name(const struct gaim_conversation
*conv
)
1236 gaim_conversation_set_logging(struct gaim_conversation
*conv
, gboolean log
)
1241 conv
->logging
= log
;
1243 gaim_conversation_update(conv
, GAIM_CONV_UPDATE_LOGGING
);
1247 gaim_conversation_is_logging(const struct gaim_conversation
*conv
)
1252 return conv
->logging
;
1256 gaim_conversation_get_send_history(const struct gaim_conversation
*conv
)
1261 return conv
->send_history
;
1265 gaim_conversation_set_history(struct gaim_conversation
*conv
,
1271 conv
->history
= history
;
1275 gaim_conversation_get_history(const struct gaim_conversation
*conv
)
1280 return conv
->history
;
1283 struct gaim_window
*
1284 gaim_conversation_get_window(const struct gaim_conversation
*conv
)
1289 return conv
->window
;
1293 gaim_conversation_get_im_data(const struct gaim_conversation
*conv
)
1298 if (gaim_conversation_get_type(conv
) != GAIM_CONV_IM
)
1305 gaim_conversation_get_chat_data(const struct gaim_conversation
*conv
)
1310 if (gaim_conversation_get_type(conv
) != GAIM_CONV_CHAT
)
1313 return conv
->u
.chat
;
1317 gaim_conversation_set_data(struct gaim_conversation
*conv
, const char *key
,
1320 if (conv
== NULL
|| key
== NULL
)
1323 g_hash_table_replace(conv
->data
, g_strdup(key
), data
);
1327 gaim_conversation_get_data(struct gaim_conversation
*conv
, const char *key
)
1329 if (conv
== NULL
|| key
== NULL
)
1332 return g_hash_table_lookup(conv
->data
, key
);
1336 gaim_get_conversations(void)
1338 return conversations
;
1348 gaim_get_chats(void)
1353 struct gaim_conversation
*
1354 gaim_find_conversation(const char *name
)
1356 struct gaim_conversation
*c
= 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
))))
1379 struct gaim_conversation
*
1380 gaim_find_conversation_with_account(const char *name
, const struct gaim_account
*account
)
1382 struct gaim_conversation
*c
= 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
)) {
1409 gaim_conversation_write(struct gaim_conversation
*conv
, const char *who
,
1410 const char *message
, size_t length
, int flags
,
1413 struct gaim_account
*account
;
1414 struct gaim_conversation_ui_ops
*ops
;
1415 struct gaim_window
*win
;
1417 GaimUnseenState unseen
;
1418 /* int logging_font_options = 0; */
1420 if (conv
== NULL
|| message
== NULL
)
1423 ops
= gaim_conversation_get_ui_ops(conv
);
1425 if (ops
== NULL
|| ops
->write_conv
== NULL
)
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
)))
1434 if (gaim_conversation_get_type(conv
) == GAIM_CONV_IM
&&
1435 !g_list_find(gaim_get_conversations(), conv
))
1438 if (gaim_conversation_get_type(conv
) == GAIM_CONV_IM
||
1439 !(account
->gc
->prpl
->options
& OPT_PROTO_UNIQUE_CHATNAME
)) {
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
;
1451 who
= account
->gc
->username
;
1454 b
= gaim_find_buddy(account
, gaim_conversation_get_name(conv
));
1457 who
= gaim_get_buddy_alias(b
);
1459 who
= gaim_conversation_get_name(conv
);
1463 b
= gaim_find_buddy(account
, who
);
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
))
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
;
1497 unseen
= GAIM_UNSEEN_TEXT
;
1500 unseen
= GAIM_UNSEEN_NONE
;
1502 gaim_conversation_set_unseen(conv
, unseen
);
1506 gaim_conversation_update_progress(struct gaim_conversation
*conv
,
1509 struct gaim_conversation_ui_ops
*ops
;
1518 * NOTE: A percent >= 1 indicates that the progress bar should be
1521 ops
= gaim_conversation_get_ui_ops(conv
);
1523 if (ops
!= NULL
&& ops
->update_progress
!= NULL
)
1524 ops
->update_progress(conv
, percent
);
1528 gaim_conversation_update(struct gaim_conversation
*conv
,
1529 GaimConvUpdateType type
)
1531 struct gaim_conversation_ui_ops
*ops
;
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
)
1555 gaim_im_set_typing_state(struct gaim_im
*im
, int state
)
1560 im
->typing_state
= state
;
1564 gaim_im_get_typing_state(const struct gaim_im
*im
)
1569 return im
->typing_state
;
1573 gaim_im_start_typing_timeout(struct gaim_im
*im
, int timeout
)
1575 struct gaim_conversation
*conv
;
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
);
1592 gaim_im_stop_typing_timeout(struct gaim_im
*im
)
1597 if (im
->typing_timeout
== 0)
1600 g_source_remove(im
->typing_timeout
);
1601 im
->typing_timeout
= 0;
1605 gaim_im_get_typing_timeout(const struct gaim_im
*im
)
1610 return im
->typing_timeout
;
1614 gaim_im_set_type_again(struct gaim_im
*im
, time_t val
)
1619 im
->type_again
= val
;
1623 gaim_im_get_type_again(const struct gaim_im
*im
)
1628 return im
->type_again
;
1632 gaim_im_start_type_again_timeout(struct gaim_im
*im
)
1637 im
->type_again_timeout
= g_timeout_add(SEND_TYPED_TIMEOUT
, send_typed
,
1638 gaim_im_get_conversation(im
));
1642 gaim_im_stop_type_again_timeout(struct gaim_im
*im
)
1647 if (im
->type_again_timeout
== 0)
1650 g_source_remove(im
->type_again_timeout
);
1651 im
->type_again_timeout
= 0;
1655 gaim_im_get_type_again_timeout(const struct gaim_im
*im
)
1660 return im
->type_again_timeout
;
1664 gaim_im_update_typing(struct gaim_im
*im
)
1669 gaim_conversation_update(gaim_im_get_conversation(im
),
1670 GAIM_CONV_UPDATE_TYPING
);
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
)
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
);
1691 gaim_conversation_write(c
, who
, message
, -1, flags
, mtime
);
1695 gaim_im_send(struct gaim_im
*im
, const char *message
)
1697 if (im
== NULL
|| message
== NULL
)
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
)
1717 gaim_chat_set_users(struct gaim_chat
*chat
, GList
*users
)
1722 chat
->in_room
= users
;
1728 gaim_chat_get_users(const struct gaim_chat
*chat
)
1733 return chat
->in_room
;
1737 gaim_chat_ignore(struct gaim_chat
*chat
, const char *name
)
1739 if (chat
== NULL
|| name
== NULL
)
1742 /* Make sure the user isn't already ignored. */
1743 if (gaim_chat_is_user_ignored(chat
, name
))
1746 gaim_chat_set_ignored(chat
,
1747 g_list_append(gaim_chat_get_ignored(chat
), g_strdup(name
)));
1751 gaim_chat_unignore(struct gaim_chat
*chat
, const char *name
)
1755 if (chat
== NULL
|| name
== NULL
)
1758 /* Make sure the user is actually ignored. */
1759 if (!gaim_chat_is_user_ignored(chat
, name
))
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
));
1769 g_list_free_1(item
);
1773 gaim_chat_set_ignored(struct gaim_chat
*chat
, GList
*ignored
)
1778 chat
->ignored
= ignored
;
1784 gaim_chat_get_ignored(const struct gaim_chat
*chat
)
1789 return chat
->ignored
;
1793 gaim_chat_get_ignored_user(const struct gaim_chat
*chat
, const char *user
)
1797 if (chat
== NULL
|| user
== NULL
)
1800 for (ignored
= gaim_chat_get_ignored(chat
);
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)))
1813 if ((*ign
== '+' && !gaim_utf8_strcasecmp(user
, ign
+ 1)) ||
1814 (*ign
!= '+' && !gaim_utf8_strcasecmp(user
, ign
)))
1823 gaim_chat_is_user_ignored(const struct gaim_chat
*chat
, const char *user
)
1825 if (chat
== NULL
|| user
== NULL
)
1828 return (gaim_chat_get_ignored_user(chat
, user
) != NULL
);
1832 gaim_chat_set_topic(struct gaim_chat
*chat
, const char *who
, const char *topic
)
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
);
1848 gaim_chat_get_topic(const struct gaim_chat
*chat
)
1857 gaim_chat_set_id(struct gaim_chat
*chat
, int id
)
1866 gaim_chat_get_id(const struct gaim_chat
*chat
)
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
)
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
))
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
)) {
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
;
1906 flags
|= WFLAG_RECV
;
1908 if (find_nick(gc
, message
))
1909 flags
|= WFLAG_NICK
;
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
);
1919 gaim_conversation_write(conv
, who
, message
, -1, flags
, mtime
);
1923 gaim_chat_send(struct gaim_chat
*chat
, const char *message
)
1925 if (chat
== NULL
|| message
== NULL
)
1928 common_send(gaim_chat_get_conversation(chat
), message
);
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
;
1939 if (chat
== NULL
|| user
== NULL
)
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
),
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
);
1960 g_snprintf(tmp
, sizeof(tmp
),
1961 _("%s [<I>%s</I>] entered the room."),
1964 gaim_conversation_write(conv
, NULL
, tmp
, -1, WFLAG_SYSTEM
, time(NULL
));
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
;
1977 if (chat
== NULL
|| old_user
== NULL
|| new_user
== NULL
)
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
);
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
));
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
));
2017 gaim_chat_remove_user(struct gaim_chat
*chat
, const char *user
,
2020 struct gaim_conversation
*conv
;
2021 struct gaim_conversation_ui_ops
*ops
;
2025 if (chat
== NULL
|| user
== NULL
)
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
);
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
));
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
);
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
)
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
)
2078 /**************************************************************************
2079 * Conversation placement functions
2080 **************************************************************************/
2081 /* This one places conversations in the last made window. */
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
;
2090 win
= gaim_get_last_window_with_type(gaim_conversation_get_type(conv
));
2093 win
= gaim_window_new();
2095 gaim_window_add_conversation(win
, conv
);
2096 gaim_window_show(win
);
2099 gaim_window_add_conversation(win
, conv
);
2102 /* This one places each conversation in its own window. */
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.
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
);
2132 conv_placement_new_window(conv
);
2134 gaim_window_add_conversation(win
, conv
);
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
));
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
;
2152 struct group
*g2
= NULL
;
2154 win2
= (struct gaim_window
*)wins
->data
;
2156 for (convs
= gaim_window_get_conversations(win2
);
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
));
2166 g2
= gaim_find_buddys_group(b2
);
2169 gaim_window_add_conversation(win2
, conv
);
2176 /* Make a new window. */
2177 conv_placement_new_window(conv
);
2181 /* This groups things by account. Otherwise, the same semantics as above */
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
);
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
);
2216 /* Make a new window. */
2217 conv_placement_new_window(conv
);
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
);
2230 conv_placement_fncs
= g_list_append(conv_placement_fncs
, data
);
2232 return gaim_conv_placement_get_fnc_count() - 1;
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
)
2256 if (conv_placement_fncs
== NULL
)
2257 ensure_default_funcs();
2259 return add_conv_placement_fnc(name
, fnc
);
2263 gaim_conv_placement_remove_fnc(int index
)
2265 struct ConvPlacementData
*data
;
2268 if (index
< 0 || index
> g_list_length(conv_placement_fncs
))
2271 node
= g_list_nth(conv_placement_fncs
, index
);
2272 data
= (struct ConvPlacementData
*)node
->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
);
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
))
2299 data
= g_list_nth_data(conv_placement_fncs
, index
);
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
))
2317 data
= g_list_nth_data(conv_placement_fncs
, index
);
2326 gaim_conv_placement_get_fnc_index(gaim_conv_placement_fnc fnc
)
2328 struct ConvPlacementData
*data
;
2332 ensure_default_funcs();
2334 for (node
= conv_placement_fncs
, i
= 0;
2336 node
= node
->next
, i
++) {
2338 data
= (struct ConvPlacementData
*)node
->data
;
2340 if (data
->fnc
== fnc
)
2348 gaim_conv_placement_get_active(void)
2350 return place_conv_index
;
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
);
2366 place_conv_index
= index
;
2370 gaim_set_win_ui_ops(struct gaim_window_ui_ops
*ops
)
2375 struct gaim_window_ui_ops
*
2376 gaim_get_win_ui_ops(void)