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"
40 struct ConvPlacementData
43 gaim_conv_placement_fnc fnc
;
46 #define SEND_TYPED_TIMEOUT 5000
48 static struct gaim_window_ui_ops
*win_ui_ops
= NULL
;
50 static GList
*conversations
= NULL
;
51 static GList
*ims
= NULL
;
52 static GList
*chats
= NULL
;
53 static GList
*windows
= NULL
;
54 static GList
*conv_placement_fncs
= NULL
;
55 static gaim_conv_placement_fnc place_conv
= NULL
;
56 static int place_conv_index
= -1;
59 insertname_compare(gconstpointer one
, gconstpointer two
)
61 const char *a
= (const char *)one
;
62 const char *b
= (const char *)two
;
65 if (*b
!= '@') return -1;
67 return strcasecmp(a
+ 1, b
+ 1);
69 } else if (*a
== '%') {
70 if (*b
!= '%') return -1;
72 return strcasecmp(a
+ 1, b
+ 1);
74 } else if (*a
== '+') {
75 if (*b
== '@') return 1;
76 if (*b
!= '+') return -1;
78 return strcasecmp(a
+ 1, b
+ 1);
80 } else if (*b
== '@' || *b
== '%' || *b
== '+')
83 return strcasecmp(a
, b
);
87 find_nick(struct gaim_connection
*gc
, const char *message
)
92 msg
= g_utf8_strdown(message
, -1);
94 who
= g_utf8_strdown(gc
->username
, -1);
97 if ((p
= strstr(msg
, who
)) != NULL
) {
98 if ((p
== msg
|| !isalnum(*(p
- 1))) && !isalnum(*(p
+ n
))) {
108 if (!gaim_utf8_strcasecmp(gc
->username
, gc
->displayname
)) {
114 who
= g_utf8_strdown(gc
->displayname
, -1);
117 if (n
> 0 && (p
= strstr(msg
, who
)) != NULL
) {
118 if ((p
== msg
|| !isalnum(*(p
- 1))) && !isalnum(*(p
+ n
))) {
133 reset_typing(gpointer data
)
135 char *name
= (char *)data
;
136 struct gaim_conversation
*c
= gaim_find_conversation(name
);
144 gaim_im_set_typing_state(im
, NOT_TYPING
);
145 gaim_im_update_typing(im
);
146 gaim_im_stop_typing_timeout(im
);
152 send_typed(gpointer data
)
154 struct gaim_conversation
*conv
= (struct gaim_conversation
*)data
;
155 struct gaim_connection
*gc
;
158 gc
= gaim_conversation_get_gc(conv
);
159 name
= gaim_conversation_get_name(conv
);
161 if (conv
!= NULL
&& gc
!= NULL
&& name
!= NULL
) {
162 gaim_im_set_type_again(GAIM_IM(conv
), TRUE
);
164 /* XXX Somebody add const stuff! */
165 serv_send_typing(gc
, (char *)name
, TYPED
);
167 gaim_debug(GAIM_DEBUG_MISC
, "conversation", "typed...\n");
174 common_send(struct gaim_conversation
*conv
, const char *message
)
176 GaimConversationType type
;
177 struct gaim_connection
*gc
;
178 struct gaim_conversation_ui_ops
*ops
;
179 char *buf
, *buf2
, *buffy
= NULL
;
181 gboolean binary
= FALSE
;
187 if ((gc
= gaim_conversation_get_gc(conv
)) == NULL
)
190 type
= gaim_conversation_get_type(conv
);
191 ops
= gaim_conversation_get_ui_ops(conv
);
193 limit
= 32 * 1024; /* You shouldn't be sending more than 32K in your
194 messages. That's a book. */
196 buf
= g_malloc(limit
);
197 strncpy(buf
, message
, limit
);
199 if (strlen(buf
) == 0) {
205 first
= g_list_first(conv
->send_history
);
210 first
->data
= g_strdup(buf
);
212 conv
->send_history
= g_list_prepend(first
, NULL
);
214 buf2
= g_malloc(limit
);
216 if (gc
->flags
& OPT_CONN_HTML
&& convo_options
& OPT_CONVO_SEND_LINKS
)
217 buffy
=linkify_text(buf
);
219 buffy
= g_strdup(buf
);
221 plugin_return
= gaim_event_broadcast(
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 gaim_event_broadcast(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 gaim_debug(GAIM_DEBUG_ERROR
, "conversation",
274 "Could not stat image %s\n",
281 * Here we check to make sure the user still wants to send
282 * the image. He may have deleted the <img> tag in which
283 * case we don't want to send the binary data.
285 filename
= img_filename
;
287 while ((c
= strchr(filename
, '/')) != NULL
)
290 g_snprintf(imgtag
, sizeof(imgtag
),
291 "<IMG SRC=\"file://%s\" ID=\"%d\" "
293 filename
, id
, (int)st
.st_size
);
295 if (strstr(buffy
, imgtag
) == 0) {
296 gaim_debug(GAIM_DEBUG_ERROR
, "conversation",
297 "Not sending image: %s\n", img_filename
);
302 length
= strlen(buffy
) + strlen("<BINARY></BINARY>");
303 bigbuf
= g_malloc(length
+ 1);
305 strlen(buffy
) + strlen("<BINARY> ") + 1,
306 "%s<BINARY>", buffy
);
308 offset
= strlen(buffy
) + strlen("<BINARY>");
312 g_snprintf(imgtag
, sizeof(imgtag
),
313 "<DATA ID=\"%d\" SIZE=\"%d\">",
314 id
, (int)st
.st_size
);
316 length
+= strlen(imgtag
) + st
.st_size
+ strlen("</DATA>");
318 bigbuf
= g_realloc(bigbuf
, length
+ 1);
320 if ((imgfile
= fopen(img_filename
, "r")) == NULL
) {
321 gaim_debug(GAIM_DEBUG_ERROR
, "conversation",
322 "Could not open image %s\n", img_filename
);
326 strncpy(bigbuf
+ offset
, imgtag
, strlen(imgtag
) + 1);
328 offset
+= strlen(imgtag
);
329 offset
+= fread(bigbuf
+ offset
, 1, st
.st_size
, imgfile
);
333 strncpy(bigbuf
+ offset
, "</DATA>",
334 strlen("</DATA>") + 1);
336 offset
+= strlen("</DATA>");
340 strncpy(bigbuf
+ offset
, "</BINARY>",
341 strlen("<BINARY>") + 1);
343 err
= serv_send_im(gc
,
344 (char *)gaim_conversation_get_name(conv
),
345 bigbuf
, length
, imflags
);
348 err
= serv_send_im(gc
,
349 (char *)gaim_conversation_get_name(conv
),
355 for (tempy
= conv
->u
.im
->images
;
357 tempy
= tempy
->next
) {
362 g_slist_free(conv
->u
.im
->images
);
363 conv
->u
.im
->images
= NULL
;
366 gaim_im_write(im
, NULL
, bigbuf
, length
,
367 WFLAG_SEND
, time(NULL
));
369 gaim_im_write(im
, NULL
, buffy
, -1, WFLAG_SEND
,
372 if (im_options
& OPT_IM_POPDOWN
)
373 gaim_window_hide(gaim_conversation_get_window(conv
));
380 err
= serv_send_im(gc
, (char *)gaim_conversation_get_name(conv
),
384 gaim_im_write(im
, NULL
, buf
, -1, WFLAG_SEND
, time(NULL
));
386 if (im_options
& OPT_IM_POPDOWN
)
387 gaim_window_hide(gaim_conversation_get_window(conv
));
395 err
= serv_chat_send(gc
, gaim_chat_get_id(GAIM_CHAT(conv
)), buf
);
403 gaim_notify_error(NULL
, NULL
,
404 _("Unable to send message. The message is "
405 "too large."), NULL
);
407 else if (err
== -ENOTCONN
) {
408 gaim_debug(GAIM_DEBUG_ERROR
, "conversation",
409 "Not yet connected.\n");
412 gaim_notify_error(NULL
, NULL
, _("Unable to send message."), NULL
);
416 if (err
> 0 && (away_options
& OPT_AWAY_BACK_ON_IM
)) {
417 if (awaymessage
!= NULL
) {
421 serv_set_away(gc
, GAIM_AWAY_CUSTOM
, NULL
);
429 update_conv_indexes(struct gaim_window
*win
)
434 for (l
= gaim_window_get_conversations(win
), i
= 0;
438 struct gaim_conversation
*conv
= (struct gaim_conversation
*)l
->data
;
440 conv
->conversation_pos
= i
;
445 gaim_window_new(void)
447 struct gaim_window
*win
;
449 win
= g_malloc0(sizeof(struct gaim_window
));
451 win
->ui_ops
= gaim_get_win_ui_ops();
453 if (win
->ui_ops
!= NULL
&& win
->ui_ops
->new_window
!= NULL
)
454 win
->ui_ops
->new_window(win
);
456 windows
= g_list_append(windows
, win
);
462 gaim_window_destroy(struct gaim_window
*win
)
464 struct gaim_window_ui_ops
*ops
;
470 ops
= gaim_window_get_ui_ops(win
);
473 * If there are any conversations in this, destroy them all. The last
474 * conversation will call gaim_window_destroy(), but this time, this
475 * check will fail and the window will actually be destroyed.
477 * This is needed because chats may not close right away. They may
478 * wait for notification first. When they get that, the window is
479 * already destroyed, and gaim either crashes or spits out gtk warnings.
480 * The problem is fixed with this check.
482 if (gaim_window_get_conversation_count(win
) > 0) {
484 node
= g_list_first(gaim_window_get_conversations(win
));
487 struct gaim_conversation
*conv
= node
->data
;
489 node
= g_list_next(node
);
491 gaim_conversation_destroy(conv
);
496 if (ops
!= NULL
&& ops
->destroy_window
!= NULL
)
497 ops
->destroy_window(win
);
499 g_list_free(gaim_window_get_conversations(win
));
501 windows
= g_list_remove(windows
, win
);
508 gaim_window_show(struct gaim_window
*win
)
510 struct gaim_window_ui_ops
*ops
;
515 ops
= gaim_window_get_ui_ops(win
);
517 if (ops
== NULL
|| ops
->show
== NULL
)
524 gaim_window_hide(struct gaim_window
*win
)
526 struct gaim_window_ui_ops
*ops
;
531 ops
= gaim_window_get_ui_ops(win
);
533 if (ops
== NULL
|| ops
->hide
== NULL
)
540 gaim_window_raise(struct gaim_window
*win
)
542 struct gaim_window_ui_ops
*ops
;
547 ops
= gaim_window_get_ui_ops(win
);
549 if (ops
== NULL
|| ops
->raise
== NULL
)
556 gaim_window_flash(struct gaim_window
*win
)
558 struct gaim_window_ui_ops
*ops
;
563 ops
= gaim_window_get_ui_ops(win
);
565 if (ops
== NULL
|| ops
->flash
== NULL
)
572 gaim_window_set_ui_ops(struct gaim_window
*win
, struct gaim_window_ui_ops
*ops
)
574 struct gaim_conversation_ui_ops
*conv_ops
= NULL
;
577 if (win
== NULL
|| win
->ui_ops
== ops
)
580 if (ops
!= NULL
&& ops
->get_conversation_ui_ops
!= NULL
)
581 conv_ops
= ops
->get_conversation_ui_ops();
583 if (win
->ui_ops
!= NULL
&& win
->ui_ops
->destroy_window
!= NULL
)
584 win
->ui_ops
->destroy_window(win
);
588 if (win
->ui_ops
!= NULL
&& win
->ui_ops
->new_window
!= NULL
)
589 win
->ui_ops
->new_window(win
);
591 for (l
= gaim_window_get_conversations(win
);
595 struct gaim_conversation
*conv
= (struct gaim_conversation
*)l
;
597 gaim_conversation_set_ui_ops(conv
, conv_ops
);
599 if (win
->ui_ops
!= NULL
&& win
->ui_ops
->add_conversation
!= NULL
)
600 win
->ui_ops
->add_conversation(win
, conv
);
604 struct gaim_window_ui_ops
*
605 gaim_window_get_ui_ops(const struct gaim_window
*win
)
614 gaim_window_add_conversation(struct gaim_window
*win
,
615 struct gaim_conversation
*conv
)
617 struct gaim_window_ui_ops
*ops
;
619 if (win
== NULL
|| conv
== NULL
)
622 if (gaim_conversation_get_window(conv
) != NULL
) {
623 gaim_window_remove_conversation(
624 gaim_conversation_get_window(conv
),
625 gaim_conversation_get_index(conv
));
628 ops
= gaim_window_get_ui_ops(win
);
630 win
->conversations
= g_list_append(win
->conversations
, conv
);
631 win
->conversation_count
++;
633 conv
->conversation_pos
= win
->conversation_count
- 1;
638 if (ops
->get_conversation_ui_ops
!= NULL
)
639 gaim_conversation_set_ui_ops(conv
, ops
->get_conversation_ui_ops());
641 if (ops
->add_conversation
!= NULL
)
642 ops
->add_conversation(win
, conv
);
645 return win
->conversation_count
- 1;
648 struct gaim_conversation
*
649 gaim_window_remove_conversation(struct gaim_window
*win
, unsigned int index
)
651 struct gaim_window_ui_ops
*ops
;
652 struct gaim_conversation
*conv
;
655 if (win
== NULL
|| index
>= gaim_window_get_conversation_count(win
))
658 ops
= gaim_window_get_ui_ops(win
);
660 node
= g_list_nth(gaim_window_get_conversations(win
), index
);
661 conv
= (struct gaim_conversation
*)node
->data
;
663 if (ops
!= NULL
&& ops
->remove_conversation
!= NULL
)
664 ops
->remove_conversation(win
, conv
);
666 win
->conversations
= g_list_remove_link(win
->conversations
, node
);
670 win
->conversation_count
--;
674 if (gaim_window_get_conversation_count(win
) == 0)
675 gaim_window_destroy(win
);
677 /* Change all the indexes. */
678 update_conv_indexes(win
);
685 gaim_window_move_conversation(struct gaim_window
*win
, unsigned int index
,
686 unsigned int new_index
)
688 struct gaim_window_ui_ops
*ops
;
689 struct gaim_conversation
*conv
;
692 if (win
== NULL
|| index
>= gaim_window_get_conversation_count(win
) ||
696 /* We can't move this past the last index. */
697 if (new_index
> gaim_window_get_conversation_count(win
))
698 new_index
= gaim_window_get_conversation_count(win
);
700 /* Get the list item for this conversation at its current index. */
701 l
= g_list_nth(gaim_window_get_conversations(win
), index
);
704 /* Should never happen. */
705 gaim_debug(GAIM_DEBUG_ERROR
, "conversation",
706 "Misordered conversations list in window %p\n", win
);
711 conv
= (struct gaim_conversation
*)l
->data
;
713 /* Update the UI part of this. */
714 ops
= gaim_window_get_ui_ops(win
);
716 if (ops
!= NULL
&& ops
->move_conversation
!= NULL
)
717 ops
->move_conversation(win
, conv
, new_index
);
719 if (new_index
> index
)
722 /* Remove the old one. */
723 win
->conversations
= g_list_delete_link(win
->conversations
, l
);
725 /* Insert it where it should go. */
726 win
->conversations
= g_list_insert(win
->conversations
, conv
, new_index
);
728 update_conv_indexes(win
);
731 struct gaim_conversation
*
732 gaim_window_get_conversation_at(const struct gaim_window
*win
,
735 if (win
== NULL
|| index
>= gaim_window_get_conversation_count(win
))
738 return (struct gaim_conversation
*)g_list_nth_data(
739 gaim_window_get_conversations(win
), index
);
743 gaim_window_get_conversation_count(const struct gaim_window
*win
)
748 return win
->conversation_count
;
752 gaim_window_switch_conversation(struct gaim_window
*win
, unsigned int index
)
754 struct gaim_window_ui_ops
*ops
;
756 if (win
== NULL
|| index
< 0 ||
757 index
>= gaim_window_get_conversation_count(win
))
760 ops
= gaim_window_get_ui_ops(win
);
762 if (ops
!= NULL
&& ops
->switch_conversation
!= NULL
)
763 ops
->switch_conversation(win
, index
);
765 gaim_conversation_set_unseen(
766 gaim_window_get_conversation_at(win
, index
), 0);
769 struct gaim_conversation
*
770 gaim_window_get_active_conversation(const struct gaim_window
*win
)
772 struct gaim_window_ui_ops
*ops
;
777 ops
= gaim_window_get_ui_ops(win
);
779 if (ops
!= NULL
&& ops
->get_active_index
!= NULL
)
780 return gaim_window_get_conversation_at(win
, ops
->get_active_index(win
));
786 gaim_window_get_conversations(const struct gaim_window
*win
)
791 return win
->conversations
;
795 gaim_get_windows(void)
801 gaim_get_first_window_with_type(GaimConversationType type
)
804 struct gaim_window
*win
;
805 struct gaim_conversation
*conv
;
807 if (type
== GAIM_CONV_UNKNOWN
)
810 for (wins
= gaim_get_windows(); wins
!= NULL
; wins
= wins
->next
) {
811 win
= (struct gaim_window
*)wins
->data
;
813 for (convs
= gaim_window_get_conversations(win
);
815 convs
= convs
->next
) {
817 conv
= (struct gaim_conversation
*)convs
->data
;
819 if (gaim_conversation_get_type(conv
) == type
)
828 gaim_get_last_window_with_type(GaimConversationType type
)
831 struct gaim_window
*win
;
832 struct gaim_conversation
*conv
;
834 if (type
== GAIM_CONV_UNKNOWN
)
837 for (wins
= g_list_last(gaim_get_windows());
841 win
= (struct gaim_window
*)wins
->data
;
843 for (convs
= gaim_window_get_conversations(win
);
845 convs
= convs
->next
) {
847 conv
= (struct gaim_conversation
*)convs
->data
;
849 if (gaim_conversation_get_type(conv
) == type
)
857 /**************************************************************************
859 **************************************************************************/
860 struct gaim_conversation
*
861 gaim_conversation_new(GaimConversationType type
, struct gaim_account
*account
,
864 struct gaim_conversation
*conv
;
866 if (type
== GAIM_CONV_UNKNOWN
)
869 /* Check if this conversation already exists. */
870 if ((conv
= gaim_find_conversation_with_account(name
, account
)) != NULL
)
873 conv
= g_malloc0(sizeof(struct gaim_conversation
));
876 conv
->account
= account
;
877 conv
->name
= g_strdup(name
);
878 conv
->title
= g_strdup(name
);
879 conv
->send_history
= g_list_append(NULL
, NULL
);
880 conv
->history
= g_string_new("");
881 conv
->data
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
884 if (type
== GAIM_CONV_IM
)
886 conv
->u
.im
= g_malloc0(sizeof(struct gaim_im
));
887 conv
->u
.im
->conv
= conv
;
889 ims
= g_list_append(ims
, conv
);
891 gaim_conversation_set_logging(conv
,
892 (logging_options
& OPT_LOG_CONVOS
));
894 else if (type
== GAIM_CONV_CHAT
)
896 conv
->u
.chat
= g_malloc0(sizeof(struct gaim_chat
));
897 conv
->u
.chat
->conv
= conv
;
899 chats
= g_list_append(chats
, conv
);
901 gaim_conversation_set_logging(conv
, (logging_options
& OPT_LOG_CHATS
));
904 conversations
= g_list_append(conversations
, conv
);
906 /* Auto-set the title. */
907 gaim_conversation_autoset_title(conv
);
910 * Create a window if one does not exist. If it does, use the last
913 if (windows
== NULL
||
914 (type
== GAIM_CONV_IM
&& !(im_options
& OPT_IM_ONE_WINDOW
)) ||
915 (type
== GAIM_CONV_CHAT
&& !(chat_options
& OPT_CHAT_ONE_WINDOW
))) {
916 struct gaim_window
*win
;
918 win
= gaim_window_new();
919 gaim_window_add_conversation(win
, conv
);
921 /* Ensure the window is visible. */
922 gaim_window_show(win
);
925 if (place_conv
== NULL
)
926 gaim_conv_placement_set_active(0);
931 gaim_event_broadcast(event_new_conversation
, name
);
937 gaim_conversation_destroy(struct gaim_conversation
*conv
)
939 GaimPluginProtocolInfo
*prpl_info
= NULL
;
940 struct gaim_window
*win
;
941 struct gaim_conversation_ui_ops
*ops
;
942 struct gaim_connection
*gc
;
949 win
= gaim_conversation_get_window(conv
);
950 ops
= gaim_conversation_get_ui_ops(conv
);
951 gc
= gaim_conversation_get_gc(conv
);
952 name
= gaim_conversation_get_name(conv
);
955 /* Still connected */
956 prpl_info
= GAIM_PLUGIN_PROTOCOL_INFO(gc
->prpl
);
958 if (gaim_conversation_get_type(conv
) == GAIM_CONV_IM
) {
959 if (!(misc_options
& OPT_MISC_STEALTH_TYPING
))
960 serv_send_typing(gc
, (char *)name
, NOT_TYPING
);
962 if (gc
&& prpl_info
->convo_closed
!= NULL
)
963 prpl_info
->convo_closed(gc
, (char *)name
);
965 else if (gaim_conversation_get_type(conv
) == GAIM_CONV_CHAT
) {
967 * This is unfortunately necessary, because calling serv_chat_leave()
968 * calls this gaim_conversation_destroy(), which leads to two calls
969 * here.. We can't just return after this, because then it'll return
970 * on the next pass. So, since serv_got_chat_left(), which is
971 * eventually called from the prpl that serv_chat_leave() calls,
972 * removes this conversation from the gc's buddy_chats list, we're
973 * going to check to see if this exists in the list. If so, we want
974 * to return after calling this, because it'll be called again. If not,
975 * fall through, because it'll have already been removed, and we'd
976 * be on the 2nd pass.
978 * Long paragraph. <-- Short sentence.
983 if (gc
&& g_slist_find(gc
->buddy_chats
, conv
) != NULL
) {
984 serv_chat_leave(gc
, gaim_chat_get_id(GAIM_CHAT(conv
)));
991 gaim_event_broadcast(event_del_conversation
, conv
);
993 if (conv
->name
!= NULL
) g_free(conv
->name
);
994 if (conv
->title
!= NULL
) g_free(conv
->title
);
996 for (node
= g_list_first(conv
->send_history
);
998 node
= g_list_next(node
)) {
1000 if (node
->data
!= NULL
)
1004 g_list_free(g_list_first(conv
->send_history
));
1006 if (conv
->history
!= NULL
)
1007 g_string_free(conv
->history
, TRUE
);
1009 conversations
= g_list_remove(conversations
, conv
);
1011 if (conv
->type
== GAIM_CONV_IM
) {
1014 gaim_im_stop_typing_timeout(conv
->u
.im
);
1015 gaim_im_stop_type_again_timeout(conv
->u
.im
);
1017 for (snode
= conv
->u
.im
->images
; snode
!= NULL
; snode
= snode
->next
) {
1018 if (snode
->data
!= NULL
)
1019 g_free(snode
->data
);
1022 g_slist_free(conv
->u
.im
->images
);
1026 ims
= g_list_remove(ims
, conv
);
1028 else if (conv
->type
== GAIM_CONV_CHAT
) {
1030 for (node
= conv
->u
.chat
->in_room
; node
!= NULL
; node
= node
->next
) {
1031 if (node
->data
!= NULL
)
1035 for (node
= conv
->u
.chat
->ignored
; node
!= NULL
; node
= node
->next
) {
1036 if (node
->data
!= NULL
)
1040 g_list_free(conv
->u
.chat
->in_room
);
1041 g_list_free(conv
->u
.chat
->ignored
);
1043 if (conv
->u
.chat
->who
!= NULL
)
1044 g_free(conv
->u
.chat
->who
);
1046 if (conv
->u
.chat
->topic
!= NULL
)
1047 g_free(conv
->u
.chat
->topic
);
1049 g_free(conv
->u
.chat
);
1051 chats
= g_list_remove(chats
, conv
);
1054 g_hash_table_destroy(conv
->data
);
1057 gaim_window_remove_conversation(win
,
1058 gaim_conversation_get_index(conv
));
1061 if (ops
!= NULL
&& ops
->destroy_conversation
!= NULL
)
1062 ops
->destroy_conversation(conv
);
1067 GaimConversationType
1068 gaim_conversation_get_type(const struct gaim_conversation
*conv
)
1071 return GAIM_CONV_UNKNOWN
;
1077 gaim_conversation_set_ui_ops(struct gaim_conversation
*conv
,
1078 struct gaim_conversation_ui_ops
*ops
)
1080 if (conv
== NULL
|| conv
->ui_ops
== ops
)
1083 if (conv
->ui_ops
!= NULL
&& conv
->ui_ops
->destroy_conversation
!= NULL
)
1084 conv
->ui_ops
->destroy_conversation(conv
);
1086 conv
->ui_data
= NULL
;
1091 struct gaim_conversation_ui_ops
*
1092 gaim_conversation_get_ui_ops(struct gaim_conversation
*conv
)
1097 return conv
->ui_ops
;
1101 gaim_conversation_set_account(struct gaim_conversation
*conv
,
1102 struct gaim_account
*account
)
1104 if (conv
== NULL
|| account
== gaim_conversation_get_account(conv
))
1107 conv
->account
= account
;
1109 gaim_conversation_update(conv
, GAIM_CONV_UPDATE_ACCOUNT
);
1112 struct gaim_account
*
1113 gaim_conversation_get_account(const struct gaim_conversation
*conv
)
1118 return conv
->account
;
1121 struct gaim_connection
*
1122 gaim_conversation_get_gc(const struct gaim_conversation
*conv
)
1124 struct gaim_account
*account
;
1129 account
= gaim_conversation_get_account(conv
);
1131 if (account
== NULL
)
1138 gaim_conversation_set_title(struct gaim_conversation
*conv
, const char *title
)
1140 struct gaim_conversation_ui_ops
*ops
;
1142 if (conv
== NULL
|| title
== NULL
)
1145 if (conv
->title
!= NULL
)
1146 g_free(conv
->title
);
1148 conv
->title
= g_strdup(title
);
1150 ops
= gaim_conversation_get_ui_ops(conv
);
1152 if (ops
!= NULL
&& ops
->set_title
!= NULL
)
1153 ops
->set_title(conv
, conv
->title
);
1157 gaim_conversation_get_title(const struct gaim_conversation
*conv
)
1166 gaim_conversation_autoset_title(struct gaim_conversation
*conv
)
1168 struct gaim_account
*account
;
1170 const char *text
, *name
;
1175 account
= gaim_conversation_get_account(conv
);
1176 name
= gaim_conversation_get_name(conv
);
1178 if (((im_options
& OPT_IM_ALIAS_TAB
) == OPT_IM_ALIAS_TAB
) &&
1179 account
!= NULL
&& ((b
= gaim_find_buddy(account
, name
)) != NULL
)) {
1181 text
= gaim_get_buddy_alias(b
);
1186 gaim_conversation_set_title(conv
, text
);
1190 gaim_conversation_get_index(const struct gaim_conversation
*conv
)
1195 return conv
->conversation_pos
;
1199 gaim_conversation_set_unseen(struct gaim_conversation
*conv
,
1200 GaimUnseenState state
)
1205 conv
->unseen
= state
;
1207 gaim_conversation_update(conv
, GAIM_CONV_UPDATE_UNSEEN
);
1211 gaim_conversation_foreach(void (*func
)(struct gaim_conversation
*conv
))
1213 struct gaim_conversation
*conv
;
1219 for (l
= gaim_get_conversations(); l
!= NULL
; l
= l
->next
) {
1220 conv
= (struct gaim_conversation
*)l
->data
;
1227 gaim_conversation_get_unseen(const struct gaim_conversation
*conv
)
1232 return conv
->unseen
;
1236 gaim_conversation_get_name(const struct gaim_conversation
*conv
)
1245 gaim_conversation_set_logging(struct gaim_conversation
*conv
, gboolean log
)
1250 conv
->logging
= log
;
1252 gaim_conversation_update(conv
, GAIM_CONV_UPDATE_LOGGING
);
1256 gaim_conversation_is_logging(const struct gaim_conversation
*conv
)
1261 return conv
->logging
;
1265 gaim_conversation_get_send_history(const struct gaim_conversation
*conv
)
1270 return conv
->send_history
;
1274 gaim_conversation_set_history(struct gaim_conversation
*conv
,
1280 conv
->history
= history
;
1284 gaim_conversation_get_history(const struct gaim_conversation
*conv
)
1289 return conv
->history
;
1292 struct gaim_window
*
1293 gaim_conversation_get_window(const struct gaim_conversation
*conv
)
1298 return conv
->window
;
1302 gaim_conversation_get_im_data(const struct gaim_conversation
*conv
)
1307 if (gaim_conversation_get_type(conv
) != GAIM_CONV_IM
)
1314 gaim_conversation_get_chat_data(const struct gaim_conversation
*conv
)
1319 if (gaim_conversation_get_type(conv
) != GAIM_CONV_CHAT
)
1322 return conv
->u
.chat
;
1326 gaim_conversation_set_data(struct gaim_conversation
*conv
, const char *key
,
1329 if (conv
== NULL
|| key
== NULL
)
1332 g_hash_table_replace(conv
->data
, g_strdup(key
), data
);
1336 gaim_conversation_get_data(struct gaim_conversation
*conv
, const char *key
)
1338 if (conv
== NULL
|| key
== NULL
)
1341 return g_hash_table_lookup(conv
->data
, key
);
1345 gaim_get_conversations(void)
1347 return conversations
;
1357 gaim_get_chats(void)
1362 struct gaim_conversation
*
1363 gaim_find_conversation(const char *name
)
1365 struct gaim_conversation
*c
= NULL
;
1372 cuser
= g_strdup(normalize(name
));
1374 for (cnv
= gaim_get_conversations(); cnv
!= NULL
; cnv
= cnv
->next
) {
1375 c
= (struct gaim_conversation
*)cnv
->data
;
1377 if (!gaim_utf8_strcasecmp(cuser
, normalize(gaim_conversation_get_name(c
))))
1388 struct gaim_conversation
*
1389 gaim_find_conversation_with_account(const char *name
, const struct gaim_account
*account
)
1391 struct gaim_conversation
*c
= NULL
;
1398 cuser
= g_strdup(normalize(name
));
1400 for (cnv
= gaim_get_conversations(); cnv
!= NULL
; cnv
= cnv
->next
) {
1401 c
= (struct gaim_conversation
*)cnv
->data
;
1403 if (!gaim_utf8_strcasecmp(cuser
, normalize(gaim_conversation_get_name(c
))) &&
1404 account
== gaim_conversation_get_account(c
)) {
1418 gaim_conversation_write(struct gaim_conversation
*conv
, const char *who
,
1419 const char *message
, size_t length
, int flags
,
1422 GaimPluginProtocolInfo
*prpl_info
= NULL
;
1423 struct gaim_account
*account
;
1424 struct gaim_conversation_ui_ops
*ops
;
1425 struct gaim_window
*win
;
1427 GaimUnseenState unseen
;
1428 /* int logging_font_options = 0; */
1430 if (conv
== NULL
|| message
== NULL
)
1433 ops
= gaim_conversation_get_ui_ops(conv
);
1435 if (ops
== NULL
|| ops
->write_conv
== NULL
)
1438 account
= gaim_conversation_get_account(conv
);
1440 if (gaim_conversation_get_type(conv
) == GAIM_CONV_CHAT
&&
1441 (account
->gc
== NULL
|| !g_slist_find(account
->gc
->buddy_chats
, conv
)))
1444 if (gaim_conversation_get_type(conv
) == GAIM_CONV_IM
&&
1445 !g_list_find(gaim_get_conversations(), conv
))
1448 if (account
->gc
!= NULL
) {
1449 prpl_info
= GAIM_PLUGIN_PROTOCOL_INFO(account
->gc
->prpl
);
1451 if (gaim_conversation_get_type(conv
) == GAIM_CONV_IM
||
1452 !(prpl_info
->options
& OPT_PROTO_UNIQUE_CHATNAME
)) {
1455 if (flags
& WFLAG_SEND
) {
1456 b
= gaim_find_buddy(account
, account
->gc
->username
);
1457 if (b
!= NULL
&& strcmp(b
->name
, gaim_get_buddy_alias(b
)))
1458 who
= gaim_get_buddy_alias(b
);
1459 else if (*account
->alias
)
1460 who
= account
->alias
;
1461 else if (*account
->gc
->displayname
)
1462 who
= account
->gc
->displayname
;
1464 who
= account
->gc
->username
;
1467 b
= gaim_find_buddy(account
,
1468 gaim_conversation_get_name(conv
));
1471 who
= gaim_get_buddy_alias(b
);
1473 who
= gaim_conversation_get_name(conv
);
1477 b
= gaim_find_buddy(account
, who
);
1480 who
= gaim_get_buddy_alias(b
);
1485 ops
->write_conv(conv
, who
, message
, length
, flags
, mtime
);
1487 win
= gaim_conversation_get_window(conv
);
1489 if (!(flags
& WFLAG_NOLOG
) &&
1490 ((gaim_conversation_get_type(conv
) == GAIM_CONV_CHAT
&&
1491 (chat_options
& OPT_CHAT_POPUP
)) ||
1492 (gaim_conversation_get_type(conv
) == GAIM_CONV_IM
&&
1493 ((im_options
& OPT_IM_POPUP
) || (im_options
& OPT_IM_POPDOWN
))))) {
1495 gaim_window_show(win
);
1498 /* Tab highlighting */
1499 if (!(flags
& WFLAG_RECV
) && !(flags
& WFLAG_SYSTEM
))
1502 if (gaim_conversation_get_type(conv
) == GAIM_CONV_IM
) {
1503 if ((flags
& WFLAG_RECV
) == WFLAG_RECV
)
1504 gaim_im_set_typing_state(GAIM_IM(conv
), NOT_TYPING
);
1507 if (gaim_window_get_active_conversation(win
) != conv
) {
1508 if ((flags
& WFLAG_NICK
) == WFLAG_NICK
||
1509 gaim_conversation_get_unseen(conv
) == GAIM_UNSEEN_NICK
)
1510 unseen
= GAIM_UNSEEN_NICK
;
1512 unseen
= GAIM_UNSEEN_TEXT
;
1515 unseen
= GAIM_UNSEEN_NONE
;
1517 gaim_conversation_set_unseen(conv
, unseen
);
1521 gaim_conversation_update_progress(struct gaim_conversation
*conv
,
1524 struct gaim_conversation_ui_ops
*ops
;
1533 * NOTE: A percent >= 1 indicates that the progress bar should be
1536 ops
= gaim_conversation_get_ui_ops(conv
);
1538 if (ops
!= NULL
&& ops
->update_progress
!= NULL
)
1539 ops
->update_progress(conv
, percent
);
1543 gaim_conversation_update(struct gaim_conversation
*conv
,
1544 GaimConvUpdateType type
)
1546 struct gaim_conversation_ui_ops
*ops
;
1551 ops
= gaim_conversation_get_ui_ops(conv
);
1553 if (ops
!= NULL
&& ops
->updated
!= NULL
)
1554 ops
->updated(conv
, type
);
1557 /**************************************************************************
1558 * IM Conversation API
1559 **************************************************************************/
1560 struct gaim_conversation
*
1561 gaim_im_get_conversation(struct gaim_im
*im
)
1570 gaim_im_set_typing_state(struct gaim_im
*im
, int state
)
1575 im
->typing_state
= state
;
1579 gaim_im_get_typing_state(const struct gaim_im
*im
)
1584 return im
->typing_state
;
1588 gaim_im_start_typing_timeout(struct gaim_im
*im
, int timeout
)
1590 struct gaim_conversation
*conv
;
1596 if (im
->typing_timeout
> 0)
1597 gaim_im_stop_typing_timeout(im
);
1599 conv
= gaim_im_get_conversation(im
);
1600 name
= gaim_conversation_get_name(conv
);
1602 im
->typing_timeout
= g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE
,
1603 timeout
* 1000, reset_typing
, g_strdup(name
), g_free
);
1607 gaim_im_stop_typing_timeout(struct gaim_im
*im
)
1612 if (im
->typing_timeout
== 0)
1615 g_source_remove(im
->typing_timeout
);
1616 im
->typing_timeout
= 0;
1620 gaim_im_get_typing_timeout(const struct gaim_im
*im
)
1625 return im
->typing_timeout
;
1629 gaim_im_set_type_again(struct gaim_im
*im
, time_t val
)
1634 im
->type_again
= val
;
1638 gaim_im_get_type_again(const struct gaim_im
*im
)
1643 return im
->type_again
;
1647 gaim_im_start_type_again_timeout(struct gaim_im
*im
)
1652 im
->type_again_timeout
= g_timeout_add(SEND_TYPED_TIMEOUT
, send_typed
,
1653 gaim_im_get_conversation(im
));
1657 gaim_im_stop_type_again_timeout(struct gaim_im
*im
)
1662 if (im
->type_again_timeout
== 0)
1665 g_source_remove(im
->type_again_timeout
);
1666 im
->type_again_timeout
= 0;
1670 gaim_im_get_type_again_timeout(const struct gaim_im
*im
)
1675 return im
->type_again_timeout
;
1679 gaim_im_update_typing(struct gaim_im
*im
)
1684 gaim_conversation_update(gaim_im_get_conversation(im
),
1685 GAIM_CONV_UPDATE_TYPING
);
1689 gaim_im_write(struct gaim_im
*im
, const char *who
, const char *message
,
1690 size_t len
, int flags
, time_t mtime
)
1692 struct gaim_conversation
*c
;
1694 if (im
== NULL
|| message
== NULL
)
1697 c
= gaim_im_get_conversation(im
);
1699 /* Raise the window, if specified in prefs. */
1700 if (!(flags
& WFLAG_NOLOG
) & (im_options
& OPT_IM_POPUP
))
1701 gaim_window_raise(gaim_conversation_get_window(c
));
1703 if (c
->ui_ops
!= NULL
&& c
->ui_ops
->write_im
!= NULL
)
1704 c
->ui_ops
->write_im(c
, who
, message
, len
, flags
, mtime
);
1706 gaim_conversation_write(c
, who
, message
, -1, flags
, mtime
);
1710 gaim_im_send(struct gaim_im
*im
, const char *message
)
1712 if (im
== NULL
|| message
== NULL
)
1715 common_send(gaim_im_get_conversation(im
), message
);
1718 /**************************************************************************
1719 * Chat Conversation API
1720 **************************************************************************/
1722 struct gaim_conversation
*
1723 gaim_chat_get_conversation(struct gaim_chat
*chat
)
1732 gaim_chat_set_users(struct gaim_chat
*chat
, GList
*users
)
1737 chat
->in_room
= users
;
1743 gaim_chat_get_users(const struct gaim_chat
*chat
)
1748 return chat
->in_room
;
1752 gaim_chat_ignore(struct gaim_chat
*chat
, const char *name
)
1754 if (chat
== NULL
|| name
== NULL
)
1757 /* Make sure the user isn't already ignored. */
1758 if (gaim_chat_is_user_ignored(chat
, name
))
1761 gaim_chat_set_ignored(chat
,
1762 g_list_append(gaim_chat_get_ignored(chat
), g_strdup(name
)));
1766 gaim_chat_unignore(struct gaim_chat
*chat
, const char *name
)
1770 if (chat
== NULL
|| name
== NULL
)
1773 /* Make sure the user is actually ignored. */
1774 if (!gaim_chat_is_user_ignored(chat
, name
))
1777 item
= g_list_find(gaim_chat_get_ignored(chat
),
1778 gaim_chat_get_ignored_user(chat
, name
));
1780 gaim_chat_set_ignored(chat
,
1781 g_list_remove_link(gaim_chat_get_ignored(chat
), item
));
1784 g_list_free_1(item
);
1788 gaim_chat_set_ignored(struct gaim_chat
*chat
, GList
*ignored
)
1793 chat
->ignored
= ignored
;
1799 gaim_chat_get_ignored(const struct gaim_chat
*chat
)
1804 return chat
->ignored
;
1808 gaim_chat_get_ignored_user(const struct gaim_chat
*chat
, const char *user
)
1812 if (chat
== NULL
|| user
== NULL
)
1815 for (ignored
= gaim_chat_get_ignored(chat
);
1817 ignored
= ignored
->next
) {
1819 const char *ign
= (const char *)ignored
->data
;
1821 if (!gaim_utf8_strcasecmp(user
, ign
) ||
1822 ((*ign
== '+' || *ign
== '%') && !gaim_utf8_strcasecmp(user
, ign
+ 1)))
1828 if ((*ign
== '+' && !gaim_utf8_strcasecmp(user
, ign
+ 1)) ||
1829 (*ign
!= '+' && !gaim_utf8_strcasecmp(user
, ign
)))
1838 gaim_chat_is_user_ignored(const struct gaim_chat
*chat
, const char *user
)
1840 if (chat
== NULL
|| user
== NULL
)
1843 return (gaim_chat_get_ignored_user(chat
, user
) != NULL
);
1847 gaim_chat_set_topic(struct gaim_chat
*chat
, const char *who
, const char *topic
)
1852 if (chat
->who
!= NULL
) free(chat
->who
);
1853 if (chat
->topic
!= NULL
) free(chat
->topic
);
1855 chat
->who
= (who
== NULL
? NULL
: g_strdup(who
));
1856 chat
->topic
= (topic
== NULL
? NULL
: g_strdup(topic
));
1858 gaim_conversation_update(gaim_chat_get_conversation(chat
),
1859 GAIM_CONV_UPDATE_TOPIC
);
1863 gaim_chat_get_topic(const struct gaim_chat
*chat
)
1872 gaim_chat_set_id(struct gaim_chat
*chat
, int id
)
1881 gaim_chat_get_id(const struct gaim_chat
*chat
)
1890 gaim_chat_write(struct gaim_chat
*chat
, const char *who
,
1891 const char *message
, int flags
, time_t mtime
)
1893 struct gaim_conversation
*conv
;
1894 struct gaim_connection
*gc
;
1896 if (chat
== NULL
|| who
== NULL
|| message
== NULL
)
1899 conv
= gaim_chat_get_conversation(chat
);
1900 gc
= gaim_conversation_get_gc(conv
);
1902 /* Don't display this if the person who wrote it is ignored. */
1903 if (gaim_chat_is_user_ignored(chat
, who
))
1906 /* Raise the window, if specified in prefs. */
1907 if (!(flags
& WFLAG_NOLOG
) & (chat_options
& OPT_CHAT_POPUP
))
1908 gaim_window_raise(gaim_conversation_get_window(conv
));
1910 if (!(flags
& WFLAG_WHISPER
)) {
1913 str
= g_strdup(normalize(who
));
1915 if (!gaim_utf8_strcasecmp(str
, normalize(gc
->username
)) ||
1916 !gaim_utf8_strcasecmp(str
, normalize(gc
->displayname
))) {
1918 flags
|= WFLAG_SEND
;
1921 flags
|= WFLAG_RECV
;
1923 if (find_nick(gc
, message
))
1924 flags
|= WFLAG_NICK
;
1930 /* Pass this on to either the ops structure or the default write func. */
1931 if (conv
->ui_ops
!= NULL
&& conv
->ui_ops
->write_chat
!= NULL
)
1932 conv
->ui_ops
->write_chat(conv
, who
, message
, flags
, mtime
);
1934 gaim_conversation_write(conv
, who
, message
, -1, flags
, mtime
);
1938 gaim_chat_send(struct gaim_chat
*chat
, const char *message
)
1940 if (chat
== NULL
|| message
== NULL
)
1943 common_send(gaim_chat_get_conversation(chat
), message
);
1947 gaim_chat_add_user(struct gaim_chat
*chat
, const char *user
,
1948 const char *extra_msg
)
1950 struct gaim_conversation
*conv
;
1951 struct gaim_conversation_ui_ops
*ops
;
1954 if (chat
== NULL
|| user
== NULL
)
1957 conv
= gaim_chat_get_conversation(chat
);
1958 ops
= gaim_conversation_get_ui_ops(conv
);
1960 gaim_chat_set_users(chat
,
1961 g_list_insert_sorted(gaim_chat_get_users(chat
), g_strdup(user
),
1962 insertname_compare
));
1964 gaim_event_broadcast(event_chat_buddy_join
,
1965 gaim_conversation_get_gc(conv
), gaim_chat_get_id(chat
),
1968 if (ops
!= NULL
&& ops
->chat_add_user
!= NULL
)
1969 ops
->chat_add_user(conv
, user
);
1971 if (chat_options
& OPT_CHAT_LOGON
) {
1972 if (extra_msg
== NULL
)
1973 g_snprintf(tmp
, sizeof(tmp
), _("%s entered the room."), user
);
1975 g_snprintf(tmp
, sizeof(tmp
),
1976 _("%s [<I>%s</I>] entered the room."),
1979 gaim_conversation_write(conv
, NULL
, tmp
, -1, WFLAG_SYSTEM
, time(NULL
));
1984 gaim_chat_rename_user(struct gaim_chat
*chat
, const char *old_user
,
1985 const char *new_user
)
1987 struct gaim_conversation
*conv
;
1988 struct gaim_conversation_ui_ops
*ops
;
1992 if (chat
== NULL
|| old_user
== NULL
|| new_user
== NULL
)
1995 conv
= gaim_chat_get_conversation(chat
);
1996 ops
= gaim_conversation_get_ui_ops(conv
);
1998 gaim_chat_set_users(chat
,
1999 g_list_insert_sorted(gaim_chat_get_users(chat
), g_strdup(new_user
),
2000 insertname_compare
));
2002 if (ops
!= NULL
&& ops
->chat_rename_user
!= NULL
)
2003 ops
->chat_rename_user(conv
, old_user
, new_user
);
2005 for (names
= gaim_chat_get_users(chat
);
2007 names
= names
->next
) {
2009 if (!gaim_utf8_strcasecmp((char *)names
->data
, old_user
)) {
2010 gaim_chat_set_users(chat
,
2011 g_list_remove(gaim_chat_get_users(chat
), names
->data
));
2016 if (gaim_chat_is_user_ignored(chat
, old_user
)) {
2017 gaim_chat_unignore(chat
, old_user
);
2018 gaim_chat_ignore(chat
, new_user
);
2020 else if (gaim_chat_is_user_ignored(chat
, new_user
))
2021 gaim_chat_unignore(chat
, new_user
);
2023 if (chat_options
& OPT_CHAT_LOGON
) {
2024 g_snprintf(tmp
, sizeof(tmp
),
2025 _("%s is now known as %s"), old_user
, new_user
);
2027 gaim_conversation_write(conv
, NULL
, tmp
, -1, WFLAG_SYSTEM
, time(NULL
));
2032 gaim_chat_remove_user(struct gaim_chat
*chat
, const char *user
,
2035 struct gaim_conversation
*conv
;
2036 struct gaim_conversation_ui_ops
*ops
;
2040 if (chat
== NULL
|| user
== NULL
)
2043 conv
= gaim_chat_get_conversation(chat
);
2044 ops
= gaim_conversation_get_ui_ops(conv
);
2046 gaim_event_broadcast(event_chat_buddy_leave
, gaim_conversation_get_gc(conv
),
2047 gaim_chat_get_id(chat
), user
);
2049 if (ops
!= NULL
&& ops
->chat_remove_user
!= NULL
)
2050 ops
->chat_remove_user(conv
, user
);
2052 for (names
= gaim_chat_get_users(chat
);
2054 names
= names
->next
) {
2056 if (!gaim_utf8_strcasecmp((char *)names
->data
, user
)) {
2057 gaim_chat_set_users(chat
,
2058 g_list_remove(gaim_chat_get_users(chat
), names
->data
));
2063 /* NOTE: Don't remove them from ignored in case they re-enter. */
2065 if (chat_options
& OPT_CHAT_LOGON
) {
2066 if (reason
!= NULL
&& *reason
!= '\0')
2067 g_snprintf(tmp
, sizeof(tmp
),
2068 _("%s left the room (%s)."), user
, reason
);
2070 g_snprintf(tmp
, sizeof(tmp
), _("%s left the room."), user
);
2072 gaim_conversation_write(conv
, NULL
, tmp
, -1, WFLAG_SYSTEM
, time(NULL
));
2076 struct gaim_conversation
*
2077 gaim_find_chat(struct gaim_connection
*gc
, int id
)
2080 struct gaim_conversation
*conv
;
2082 for (l
= gaim_get_chats(); l
!= NULL
; l
= l
->next
) {
2083 conv
= (struct gaim_conversation
*)l
->data
;
2085 if (gaim_chat_get_id(GAIM_CHAT(conv
)) == id
&&
2086 gaim_conversation_get_gc(conv
) == gc
)
2093 /**************************************************************************
2094 * Conversation placement functions
2095 **************************************************************************/
2096 /* This one places conversations in the last made window. */
2098 conv_placement_last_created_win(struct gaim_conversation
*conv
)
2100 struct gaim_window
*win
;
2102 if (convo_options
& OPT_CONVO_COMBINE
)
2103 win
= g_list_last(gaim_get_windows())->data
;
2105 win
= gaim_get_last_window_with_type(gaim_conversation_get_type(conv
));
2108 win
= gaim_window_new();
2110 gaim_window_add_conversation(win
, conv
);
2111 gaim_window_show(win
);
2114 gaim_window_add_conversation(win
, conv
);
2117 /* This one places each conversation in its own window. */
2119 conv_placement_new_window(struct gaim_conversation
*conv
)
2121 struct gaim_window
*win
;
2123 win
= gaim_window_new();
2125 gaim_window_add_conversation(win
, conv
);
2127 gaim_window_show(win
);
2131 * This groups things by, well, group. Buddies from groups will always be
2132 * grouped together, and a buddy from a group not belonging to any currently
2133 * open windows will get a new window.
2136 conv_placement_by_group(struct gaim_conversation
*conv
)
2138 struct gaim_window
*win
;
2139 GaimConversationType type
;
2141 type
= gaim_conversation_get_type(conv
);
2143 if (type
!= GAIM_CONV_IM
) {
2144 win
= gaim_get_last_window_with_type(type
);
2147 conv_placement_new_window(conv
);
2149 gaim_window_add_conversation(win
, conv
);
2153 struct group
*grp
= NULL
;
2154 GList
*wins
, *convs
;
2156 b
= gaim_find_buddy(gaim_conversation_get_account(conv
),
2157 gaim_conversation_get_name(conv
));
2160 grp
= gaim_find_buddys_group(b
);
2162 /* Go through the list of IMs and find one with this group. */
2163 for (wins
= gaim_get_windows(); wins
!= NULL
; wins
= wins
->next
) {
2164 struct gaim_window
*win2
;
2165 struct gaim_conversation
*conv2
;
2167 struct group
*g2
= NULL
;
2169 win2
= (struct gaim_window
*)wins
->data
;
2171 for (convs
= gaim_window_get_conversations(win2
);
2173 convs
= convs
->next
) {
2175 conv2
= (struct gaim_conversation
*)convs
->data
;
2177 b2
= gaim_find_buddy(gaim_conversation_get_account(conv2
),
2178 gaim_conversation_get_name(conv2
));
2181 g2
= gaim_find_buddys_group(b2
);
2184 gaim_window_add_conversation(win2
, conv
);
2191 /* Make a new window. */
2192 conv_placement_new_window(conv
);
2196 /* This groups things by account. Otherwise, the same semantics as above */
2198 conv_placement_by_account(struct gaim_conversation
*conv
)
2200 GaimConversationType type
;
2201 GList
*wins
, *convs
;
2202 struct gaim_account
*account
;
2205 account
= gaim_conversation_get_account(conv
);
2206 type
= gaim_conversation_get_type(conv
);
2209 /* Go through the list of IMs and find one with this group. */
2210 for (wins
= gaim_get_windows(); wins
!= NULL
; wins
= wins
->next
) {
2211 struct gaim_window
*win2
;
2212 struct gaim_conversation
*conv2
;
2214 win2
= (struct gaim_window
*)wins
->data
;
2216 for (convs
= gaim_window_get_conversations(win2
);
2218 convs
= convs
->next
) {
2220 conv2
= (struct gaim_conversation
*)convs
->data
;
2222 if (((convo_options
& OPT_CONVO_COMBINE
) ||
2223 type
== gaim_conversation_get_type(conv2
)) &&
2224 account
== gaim_conversation_get_account(conv2
)) {
2225 gaim_window_add_conversation(win2
, conv
);
2231 /* Make a new window. */
2232 conv_placement_new_window(conv
);
2236 add_conv_placement_fnc(const char *name
, gaim_conv_placement_fnc fnc
)
2238 struct ConvPlacementData
*data
;
2240 data
= g_malloc0(sizeof(struct ConvPlacementData
));
2242 data
->name
= g_strdup(name
);
2245 conv_placement_fncs
= g_list_append(conv_placement_fncs
, data
);
2247 return gaim_conv_placement_get_fnc_count() - 1;
2251 ensure_default_funcs(void)
2253 if (conv_placement_fncs
== NULL
) {
2254 add_conv_placement_fnc(_("Last created window"),
2255 conv_placement_last_created_win
);
2256 add_conv_placement_fnc(_("New window"),
2257 conv_placement_new_window
);
2258 add_conv_placement_fnc(_("By group"),
2259 conv_placement_by_group
);
2260 add_conv_placement_fnc(_("By account"),
2261 conv_placement_by_account
);
2266 gaim_conv_placement_add_fnc(const char *name
, gaim_conv_placement_fnc fnc
)
2268 if (name
== NULL
|| fnc
== NULL
)
2271 if (conv_placement_fncs
== NULL
)
2272 ensure_default_funcs();
2274 return add_conv_placement_fnc(name
, fnc
);
2278 gaim_conv_placement_remove_fnc(int index
)
2280 struct ConvPlacementData
*data
;
2283 if (index
< 0 || index
> g_list_length(conv_placement_fncs
))
2286 node
= g_list_nth(conv_placement_fncs
, index
);
2287 data
= (struct ConvPlacementData
*)node
->data
;
2292 conv_placement_fncs
= g_list_remove_link(conv_placement_fncs
, node
);
2293 g_list_free_1(node
);
2297 gaim_conv_placement_get_fnc_count(void)
2299 ensure_default_funcs();
2301 return g_list_length(conv_placement_fncs
);
2305 gaim_conv_placement_get_name(int index
)
2307 struct ConvPlacementData
*data
;
2309 ensure_default_funcs();
2311 if (index
< 0 || index
> g_list_length(conv_placement_fncs
))
2314 data
= g_list_nth_data(conv_placement_fncs
, index
);
2322 gaim_conv_placement_fnc
2323 gaim_conv_placement_get_fnc(int index
)
2325 struct ConvPlacementData
*data
;
2327 ensure_default_funcs();
2329 if (index
< 0 || index
> g_list_length(conv_placement_fncs
))
2332 data
= g_list_nth_data(conv_placement_fncs
, index
);
2341 gaim_conv_placement_get_fnc_index(gaim_conv_placement_fnc fnc
)
2343 struct ConvPlacementData
*data
;
2347 ensure_default_funcs();
2349 for (node
= conv_placement_fncs
, i
= 0;
2351 node
= node
->next
, i
++) {
2353 data
= (struct ConvPlacementData
*)node
->data
;
2355 if (data
->fnc
== fnc
)
2363 gaim_conv_placement_get_active(void)
2365 return place_conv_index
;
2369 gaim_conv_placement_set_active(int index
)
2371 gaim_conv_placement_fnc fnc
;
2373 ensure_default_funcs();
2375 fnc
= gaim_conv_placement_get_fnc(index
);
2381 place_conv_index
= index
;
2385 gaim_set_win_ui_ops(struct gaim_window_ui_ops
*ops
)
2390 struct gaim_window_ui_ops
*
2391 gaim_get_win_ui_ops(void)