Standardize all protocol header guard macros.
[pidgin-git.git] / libpurple / conversationtypes.c
blob47b61325679c4cdc9a79d14fb5c2dd46f1e84def
1 /*
2 * purple
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
23 #include "glibcompat.h"
24 #include "conversationtypes.h"
25 #include "debug.h"
26 #include "enums.h"
28 #define SEND_TYPED_TIMEOUT_SECONDS 5
30 /**************************************************************************/
31 /* PurpleIMConversation */
32 /**************************************************************************/
35 * Data specific to Instant Messages.
37 typedef struct
39 PurpleIMTypingState typing_state; /* The current typing state. */
40 guint typing_timeout; /* The typing timer handle. */
41 time_t type_again; /* The type again time. */
42 guint send_typed_timeout; /* The type again timer handle. */
43 PurpleBuddyIcon *icon; /* The buddy icon. */
44 } PurpleIMConversationPrivate;
46 /* IM Property enums */
47 enum {
48 IM_PROP_0,
49 IM_PROP_TYPING_STATE,
50 IM_PROP_ICON,
51 IM_PROP_LAST
54 static GParamSpec *im_properties[IM_PROP_LAST];
56 G_DEFINE_TYPE_WITH_PRIVATE(PurpleIMConversation, purple_im_conversation,
57 PURPLE_TYPE_CONVERSATION);
59 /**************************************************************************/
60 /* PurpleChatConversation */
61 /**************************************************************************/
64 * Data specific to Chats.
66 typedef struct
68 GList *ignored; /* Ignored users. */
69 char *who; /* The person who set the topic. */
70 char *topic; /* The topic. */
71 int id; /* The chat ID. */
72 char *nick; /* Your nick in this chat. */
73 gboolean left; /* We left the chat and kept the window open */
74 GHashTable *users; /* Hash table of the users in the room. */
75 } PurpleChatConversationPrivate;
77 /* Chat Property enums */
78 enum {
79 CHAT_PROP_0,
80 CHAT_PROP_TOPIC_WHO,
81 CHAT_PROP_TOPIC,
82 CHAT_PROP_ID,
83 CHAT_PROP_NICK,
84 CHAT_PROP_LEFT,
85 CHAT_PROP_LAST
88 static GParamSpec *chat_properties[CHAT_PROP_LAST];
90 G_DEFINE_TYPE_WITH_PRIVATE(PurpleChatConversation, purple_chat_conversation,
91 PURPLE_TYPE_CONVERSATION);
93 /**************************************************************************/
94 /* PurpleChatUser */
95 /**************************************************************************/
97 /**
98 * PurpleChatUser:
99 * @ui_data: The UI data associated with this chat user.
101 * Structure representing a chat user instance.
103 struct _PurpleChatUser
105 GObject gparent;
107 /*< public >*/
108 gpointer ui_data;
112 * Data for "Chat Buddies"
114 typedef struct
116 PurpleChatConversation *chat; /* The chat */
117 char *name; /* The chat participant's name in the
118 chat. */
119 char *alias; /* The chat participant's alias, if known;
120 NULL otherwise. */
121 char *alias_key; /* A string by which this user will be
122 sorted, or @c NULL if the user should be
123 sorted by its @name.
124 (This is currently always NULL. */
125 gboolean buddy; /* TRUE if this chat participant is on
126 the buddy list; FALSE otherwise. */
127 PurpleChatUserFlags flags; /* A bitwise OR of flags for this
128 participant, such as whether they
129 are a channel operator. */
130 } PurpleChatUserPrivate;
132 /* Chat User Property enums */
133 enum {
134 CU_PROP_0,
135 CU_PROP_CHAT,
136 CU_PROP_NAME,
137 CU_PROP_ALIAS,
138 CU_PROP_FLAGS,
139 CU_PROP_LAST
142 static GParamSpec *cu_properties[CU_PROP_LAST];
144 G_DEFINE_TYPE_WITH_PRIVATE(PurpleChatUser, purple_chat_user,
145 G_TYPE_OBJECT);
147 static int purple_chat_user_compare(PurpleChatUser *a,
148 PurpleChatUser *b);
150 /**************************************************************************
151 * IM Conversation API
152 **************************************************************************/
153 static gboolean
154 reset_typing_cb(gpointer data)
156 PurpleIMConversation *im = (PurpleIMConversation *)data;
158 purple_im_conversation_set_typing_state(im, PURPLE_IM_NOT_TYPING);
159 purple_im_conversation_stop_typing_timeout(im);
161 return FALSE;
164 static gboolean
165 send_typed_cb(gpointer data)
167 PurpleIMConversation *im = PURPLE_IM_CONVERSATION(data);
168 PurpleConnection *gc;
169 const char *name;
171 g_return_val_if_fail(im != NULL, FALSE);
173 gc = purple_conversation_get_connection(PURPLE_CONVERSATION(im));
174 name = purple_conversation_get_name(PURPLE_CONVERSATION(im));
176 if (gc != NULL && name != NULL) {
177 /* We set this to 1 so that PURPLE_IM_TYPING will be sent
178 * if the Purple user types anything else.
180 purple_im_conversation_set_type_again(im, 1);
182 purple_serv_send_typing(gc, name, PURPLE_IM_TYPED);
184 purple_debug(PURPLE_DEBUG_MISC, "conversationtypes", "typed...\n");
187 return FALSE;
190 void
191 purple_im_conversation_set_icon(PurpleIMConversation *im, PurpleBuddyIcon *icon)
193 PurpleIMConversationPrivate *priv = NULL;
195 g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
197 priv = purple_im_conversation_get_instance_private(im);
198 if (priv->icon != icon)
200 purple_buddy_icon_unref(priv->icon);
202 priv->icon = (icon == NULL ? NULL : purple_buddy_icon_ref(icon));
204 g_object_notify_by_pspec(G_OBJECT(im), im_properties[IM_PROP_ICON]);
207 purple_conversation_update(PURPLE_CONVERSATION(im),
208 PURPLE_CONVERSATION_UPDATE_ICON);
211 PurpleBuddyIcon *
212 purple_im_conversation_get_icon(PurpleIMConversation *im)
214 PurpleIMConversationPrivate *priv = NULL;
216 g_return_val_if_fail(PURPLE_IS_IM_CONVERSATION(im), NULL);
218 priv = purple_im_conversation_get_instance_private(im);
219 return priv->icon;
222 void
223 purple_im_conversation_set_typing_state(PurpleIMConversation *im, PurpleIMTypingState state)
225 PurpleIMConversationPrivate *priv = NULL;
226 PurpleAccount *account;
227 const char *name;
229 g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
231 priv = purple_im_conversation_get_instance_private(im);
232 name = purple_conversation_get_name(PURPLE_CONVERSATION(im));
233 account = purple_conversation_get_account(PURPLE_CONVERSATION(im));
235 if (priv->typing_state != state)
237 priv->typing_state = state;
239 g_object_notify_by_pspec(G_OBJECT(im),
240 im_properties[IM_PROP_TYPING_STATE]);
242 switch (state)
244 case PURPLE_IM_TYPING:
245 purple_signal_emit(purple_conversations_get_handle(),
246 "buddy-typing", account, name);
247 break;
248 case PURPLE_IM_TYPED:
249 purple_signal_emit(purple_conversations_get_handle(),
250 "buddy-typed", account, name);
251 break;
252 case PURPLE_IM_NOT_TYPING:
253 purple_signal_emit(purple_conversations_get_handle(),
254 "buddy-typing-stopped", account, name);
255 break;
258 purple_im_conversation_update_typing(im);
262 PurpleIMTypingState
263 purple_im_conversation_get_typing_state(PurpleIMConversation *im)
265 PurpleIMConversationPrivate *priv = NULL;
267 g_return_val_if_fail(PURPLE_IS_IM_CONVERSATION(im), 0);
269 priv = purple_im_conversation_get_instance_private(im);
270 return priv->typing_state;
273 void
274 purple_im_conversation_start_typing_timeout(PurpleIMConversation *im, int timeout)
276 PurpleIMConversationPrivate *priv = NULL;
278 g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
280 priv = purple_im_conversation_get_instance_private(im);
281 if (priv->typing_timeout > 0)
282 purple_im_conversation_stop_typing_timeout(im);
284 priv->typing_timeout = g_timeout_add_seconds(timeout, reset_typing_cb, im);
287 void
288 purple_im_conversation_stop_typing_timeout(PurpleIMConversation *im)
290 PurpleIMConversationPrivate *priv = NULL;
292 g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
294 priv = purple_im_conversation_get_instance_private(im);
295 if (priv->typing_timeout == 0)
296 return;
298 g_source_remove(priv->typing_timeout);
299 priv->typing_timeout = 0;
302 guint
303 purple_im_conversation_get_typing_timeout(PurpleIMConversation *im)
305 PurpleIMConversationPrivate *priv = NULL;
307 g_return_val_if_fail(PURPLE_IS_IM_CONVERSATION(im), 0);
309 priv = purple_im_conversation_get_instance_private(im);
310 return priv->typing_timeout;
313 void
314 purple_im_conversation_set_type_again(PurpleIMConversation *im, unsigned int val)
316 PurpleIMConversationPrivate *priv = NULL;
318 g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
320 priv = purple_im_conversation_get_instance_private(im);
321 if (val == 0)
322 priv->type_again = 0;
323 else
324 priv->type_again = time(NULL) + val;
327 time_t
328 purple_im_conversation_get_type_again(PurpleIMConversation *im)
330 PurpleIMConversationPrivate *priv = NULL;
332 g_return_val_if_fail(PURPLE_IS_IM_CONVERSATION(im), 0);
334 priv = purple_im_conversation_get_instance_private(im);
335 return priv->type_again;
338 void
339 purple_im_conversation_start_send_typed_timeout(PurpleIMConversation *im)
341 PurpleIMConversationPrivate *priv = NULL;
343 g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
345 priv = purple_im_conversation_get_instance_private(im);
346 priv->send_typed_timeout = g_timeout_add_seconds(SEND_TYPED_TIMEOUT_SECONDS,
347 send_typed_cb, im);
350 void
351 purple_im_conversation_stop_send_typed_timeout(PurpleIMConversation *im)
353 PurpleIMConversationPrivate *priv = NULL;
355 g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
357 priv = purple_im_conversation_get_instance_private(im);
358 if (priv->send_typed_timeout == 0)
359 return;
361 g_source_remove(priv->send_typed_timeout);
362 priv->send_typed_timeout = 0;
365 guint
366 purple_im_conversation_get_send_typed_timeout(PurpleIMConversation *im)
368 PurpleIMConversationPrivate *priv = NULL;
370 g_return_val_if_fail(PURPLE_IS_IM_CONVERSATION(im), 0);
372 priv = purple_im_conversation_get_instance_private(im);
373 return priv->send_typed_timeout;
376 void
377 purple_im_conversation_update_typing(PurpleIMConversation *im)
379 g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
381 purple_conversation_update(PURPLE_CONVERSATION(im),
382 PURPLE_CONVERSATION_UPDATE_TYPING);
385 static void
386 im_conversation_write_message(PurpleConversation *conv, PurpleMessage *msg)
388 PurpleIMConversation *im = PURPLE_IM_CONVERSATION(conv);
389 gboolean is_recv;
391 g_return_if_fail(im != NULL);
392 g_return_if_fail(msg != NULL);
394 is_recv = (purple_message_get_flags(msg) & PURPLE_MESSAGE_RECV);
396 if (is_recv)
397 purple_im_conversation_set_typing_state(im, PURPLE_IM_NOT_TYPING);
399 _purple_conversation_write_common(conv, msg);
402 /**************************************************************************
403 * GObject code for IMs
404 **************************************************************************/
406 /* Set method for GObject properties */
407 static void
408 purple_im_conversation_set_property(GObject *obj, guint param_id, const GValue *value,
409 GParamSpec *pspec)
411 PurpleIMConversation *im = PURPLE_IM_CONVERSATION(obj);
413 switch (param_id) {
414 case IM_PROP_TYPING_STATE:
415 purple_im_conversation_set_typing_state(im, g_value_get_enum(value));
416 break;
417 case IM_PROP_ICON:
418 purple_im_conversation_set_icon(im, g_value_get_pointer(value));
419 break;
420 default:
421 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
422 break;
426 /* Get method for GObject properties */
427 static void
428 purple_im_conversation_get_property(GObject *obj, guint param_id, GValue *value,
429 GParamSpec *pspec)
431 PurpleIMConversation *im = PURPLE_IM_CONVERSATION(obj);
433 switch (param_id) {
434 case IM_PROP_TYPING_STATE:
435 g_value_set_enum(value, purple_im_conversation_get_typing_state(im));
436 break;
437 case IM_PROP_ICON:
438 g_value_set_pointer(value, purple_im_conversation_get_icon(im));
439 break;
440 default:
441 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
442 break;
446 /* GObject initialization function */
447 static void purple_im_conversation_init(PurpleIMConversation *im)
451 /* Called when done constructing */
452 static void
453 purple_im_conversation_constructed(GObject *object)
455 PurpleIMConversation *im = PURPLE_IM_CONVERSATION(object);
456 PurpleAccount *account;
457 PurpleBuddyIcon *icon;
458 gchar *name;
460 G_OBJECT_CLASS(purple_im_conversation_parent_class)->
461 constructed(object);
463 g_object_get(object,
464 "account", &account,
465 "name", &name,
466 NULL);
468 if ((icon = purple_buddy_icons_find(account, name)))
470 purple_im_conversation_set_icon(im, icon);
471 /* purple_im_conversation_set_icon refs the icon. */
472 purple_buddy_icon_unref(icon);
475 if (purple_prefs_get_bool("/purple/logging/log_ims"))
476 purple_conversation_set_logging(PURPLE_CONVERSATION(im), TRUE);
478 g_object_unref(account);
479 g_free(name);
482 /* GObject dispose function */
483 static void
484 purple_im_conversation_dispose(GObject *object)
486 PurpleIMConversationPrivate *priv =
487 purple_im_conversation_get_instance_private(
488 PURPLE_IM_CONVERSATION(object));
490 if (priv->icon) {
491 purple_buddy_icon_unref(priv->icon);
492 priv->icon = NULL;
495 G_OBJECT_CLASS(purple_im_conversation_parent_class)->dispose(object);
498 /* GObject finalize function */
499 static void
500 purple_im_conversation_finalize(GObject *object)
502 PurpleIMConversation *im = PURPLE_IM_CONVERSATION(object);
503 PurpleConnection *gc = purple_conversation_get_connection(PURPLE_CONVERSATION(im));
504 PurpleProtocol *protocol = NULL;
505 const char *name = purple_conversation_get_name(PURPLE_CONVERSATION(im));
507 if (gc != NULL)
509 /* Still connected */
510 protocol = purple_connection_get_protocol(gc);
512 if (purple_prefs_get_bool("/purple/conversations/im/send_typing"))
513 purple_serv_send_typing(gc, name, PURPLE_IM_NOT_TYPING);
515 purple_protocol_client_iface_convo_closed(protocol, gc, name);
518 purple_im_conversation_stop_typing_timeout(im);
519 purple_im_conversation_stop_send_typed_timeout(im);
521 G_OBJECT_CLASS(purple_im_conversation_parent_class)->finalize(object);
524 /* Class initializer function */
525 static void purple_im_conversation_class_init(PurpleIMConversationClass *klass)
527 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
528 PurpleConversationClass *conv_class = PURPLE_CONVERSATION_CLASS(klass);
530 obj_class->dispose = purple_im_conversation_dispose;
531 obj_class->finalize = purple_im_conversation_finalize;
532 obj_class->constructed = purple_im_conversation_constructed;
534 /* Setup properties */
535 obj_class->get_property = purple_im_conversation_get_property;
536 obj_class->set_property = purple_im_conversation_set_property;
538 conv_class->write_message = im_conversation_write_message;
540 im_properties[IM_PROP_TYPING_STATE] = g_param_spec_enum("typing-state",
541 "Typing state",
542 "Status of the user's typing of a message.",
543 PURPLE_TYPE_IM_TYPING_STATE, PURPLE_IM_NOT_TYPING,
544 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
545 G_PARAM_STATIC_STRINGS);
547 im_properties[IM_PROP_ICON] = g_param_spec_pointer("icon",
548 "Buddy icon",
549 "The buddy icon for the IM.",
550 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
551 G_PARAM_STATIC_STRINGS);
553 g_object_class_install_properties(obj_class, IM_PROP_LAST, im_properties);
556 PurpleIMConversation *
557 purple_im_conversation_new(PurpleAccount *account, const char *name)
559 PurpleIMConversation *im;
560 PurpleConnection *gc;
562 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
563 g_return_val_if_fail(name != NULL, NULL);
565 /* Check if this conversation already exists. */
566 if ((im = purple_conversations_find_im_with_account(name, account)) != NULL)
567 return im;
569 gc = purple_account_get_connection(account);
570 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
572 im = g_object_new(PURPLE_TYPE_IM_CONVERSATION,
573 "account", account,
574 "name", name,
575 "title", name,
576 NULL);
578 return im;
581 /**************************************************************************
582 * Chat Conversation API
583 **************************************************************************/
584 static guint
585 _purple_conversation_user_hash(gconstpointer data)
587 const gchar *name = data;
588 gchar *collated;
589 guint hash;
591 collated = g_utf8_collate_key(name, -1);
592 hash = g_str_hash(collated);
593 g_free(collated);
594 return hash;
597 static gboolean
598 _purple_conversation_user_equal(gconstpointer a, gconstpointer b)
600 return !g_utf8_collate(a, b);
603 GList *
604 purple_chat_conversation_get_users(PurpleChatConversation *chat)
606 PurpleChatConversationPrivate *priv = NULL;
608 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
610 priv = purple_chat_conversation_get_instance_private(chat);
611 return g_hash_table_get_values(priv->users);
614 guint
615 purple_chat_conversation_get_users_count(PurpleChatConversation *chat)
617 PurpleChatConversationPrivate *priv = NULL;
619 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), 0);
621 priv = purple_chat_conversation_get_instance_private(chat);
622 return g_hash_table_size(priv->users);
625 void
626 purple_chat_conversation_ignore(PurpleChatConversation *chat, const char *name)
628 PurpleChatConversationPrivate *priv = NULL;
630 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
631 g_return_if_fail(name != NULL);
633 priv = purple_chat_conversation_get_instance_private(chat);
635 /* Make sure the user isn't already ignored. */
636 if (purple_chat_conversation_is_ignored_user(chat, name))
637 return;
639 purple_chat_conversation_set_ignored(chat,
640 g_list_append(priv->ignored, g_strdup(name)));
643 void
644 purple_chat_conversation_unignore(PurpleChatConversation *chat, const char *name)
646 PurpleChatConversationPrivate *priv = NULL;
647 GList *item;
649 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
650 g_return_if_fail(name != NULL);
652 priv = purple_chat_conversation_get_instance_private(chat);
654 /* Make sure the user is actually ignored. */
655 if (!purple_chat_conversation_is_ignored_user(chat, name))
656 return;
658 item = g_list_find(purple_chat_conversation_get_ignored(chat),
659 purple_chat_conversation_get_ignored_user(chat, name));
661 purple_chat_conversation_set_ignored(chat,
662 g_list_remove_link(priv->ignored, item));
664 g_free(item->data);
665 g_list_free_1(item);
668 GList *
669 purple_chat_conversation_set_ignored(PurpleChatConversation *chat, GList *ignored)
671 PurpleChatConversationPrivate *priv = NULL;
673 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
675 priv = purple_chat_conversation_get_instance_private(chat);
676 priv->ignored = ignored;
677 return ignored;
680 GList *
681 purple_chat_conversation_get_ignored(PurpleChatConversation *chat)
683 PurpleChatConversationPrivate *priv = NULL;
685 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
687 priv = purple_chat_conversation_get_instance_private(chat);
688 return priv->ignored;
691 const char *
692 purple_chat_conversation_get_ignored_user(PurpleChatConversation *chat, const char *user)
694 GList *ignored;
696 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
697 g_return_val_if_fail(user != NULL, NULL);
699 for (ignored = purple_chat_conversation_get_ignored(chat);
700 ignored != NULL;
701 ignored = ignored->next) {
703 const char *ign = (const char *)ignored->data;
705 if (!purple_utf8_strcasecmp(user, ign) ||
706 ((*ign == '+' || *ign == '%') && !purple_utf8_strcasecmp(user, ign + 1)))
707 return ign;
709 if (*ign == '@') {
710 ign++;
712 if ((*ign == '+' && !purple_utf8_strcasecmp(user, ign + 1)) ||
713 (*ign != '+' && !purple_utf8_strcasecmp(user, ign)))
714 return ign;
718 return NULL;
721 gboolean
722 purple_chat_conversation_is_ignored_user(PurpleChatConversation *chat, const char *user)
724 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), FALSE);
725 g_return_val_if_fail(user != NULL, FALSE);
727 return (purple_chat_conversation_get_ignored_user(chat, user) != NULL);
730 void
731 purple_chat_conversation_set_topic(PurpleChatConversation *chat, const char *who, const char *topic)
733 PurpleChatConversationPrivate *priv = NULL;
734 GObject *obj;
736 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
738 priv = purple_chat_conversation_get_instance_private(chat);
740 g_free(priv->who);
741 g_free(priv->topic);
743 priv->who = g_strdup(who);
744 priv->topic = g_strdup(topic);
746 obj = G_OBJECT(chat);
747 g_object_freeze_notify(obj);
748 g_object_notify_by_pspec(obj, chat_properties[CHAT_PROP_TOPIC_WHO]);
749 g_object_notify_by_pspec(obj, chat_properties[CHAT_PROP_TOPIC]);
750 g_object_thaw_notify(obj);
752 purple_conversation_update(PURPLE_CONVERSATION(chat),
753 PURPLE_CONVERSATION_UPDATE_TOPIC);
755 purple_signal_emit(purple_conversations_get_handle(), "chat-topic-changed",
756 chat, priv->who, priv->topic);
759 const char *
760 purple_chat_conversation_get_topic(PurpleChatConversation *chat)
762 PurpleChatConversationPrivate *priv = NULL;
764 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
766 priv = purple_chat_conversation_get_instance_private(chat);
767 return priv->topic;
770 const char *
771 purple_chat_conversation_get_topic_who(PurpleChatConversation *chat)
773 PurpleChatConversationPrivate *priv = NULL;
775 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
777 priv = purple_chat_conversation_get_instance_private(chat);
778 return priv->who;
781 void
782 purple_chat_conversation_set_id(PurpleChatConversation *chat, int id)
784 PurpleChatConversationPrivate *priv = NULL;
786 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
788 priv = purple_chat_conversation_get_instance_private(chat);
789 priv->id = id;
791 g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_ID]);
795 purple_chat_conversation_get_id(PurpleChatConversation *chat)
797 PurpleChatConversationPrivate *priv = NULL;
799 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), -1);
801 priv = purple_chat_conversation_get_instance_private(chat);
802 return priv->id;
805 static void
806 chat_conversation_write_message(PurpleConversation *conv, PurpleMessage *msg)
808 PurpleChatConversationPrivate *priv = NULL;
809 PurpleMessageFlags flags;
811 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(conv));
812 g_return_if_fail(msg != NULL);
814 priv = purple_chat_conversation_get_instance_private(PURPLE_CHAT_CONVERSATION(conv));
816 /* Don't display this if the person who wrote it is ignored. */
817 if (purple_message_get_author(msg) && purple_chat_conversation_is_ignored_user(
818 PURPLE_CHAT_CONVERSATION(conv), purple_message_get_author(msg)))
820 return;
823 flags = purple_message_get_flags(msg);
824 if (flags & PURPLE_MESSAGE_RECV) {
825 if (purple_utf8_has_word(purple_message_get_contents(msg), priv->nick)) {
826 flags |= PURPLE_MESSAGE_NICK;
827 purple_message_set_flags(msg, flags);
831 _purple_conversation_write_common(conv, msg);
834 void
835 purple_chat_conversation_add_user(PurpleChatConversation *chat, const char *user,
836 const char *extra_msg, PurpleChatUserFlags flags,
837 gboolean new_arrival)
839 GList *users = g_list_append(NULL, (char *)user);
840 GList *extra_msgs = g_list_append(NULL, (char *)extra_msg);
841 GList *flags2 = g_list_append(NULL, GINT_TO_POINTER(flags));
843 purple_chat_conversation_add_users(chat, users, extra_msgs, flags2, new_arrival);
845 g_list_free(users);
846 g_list_free(extra_msgs);
847 g_list_free(flags2);
850 void
851 purple_chat_conversation_add_users(PurpleChatConversation *chat, GList *users, GList *extra_msgs,
852 GList *flags, gboolean new_arrivals)
854 PurpleConversation *conv;
855 PurpleConversationUiOps *ops;
856 PurpleChatUser *chatuser;
857 PurpleChatConversationPrivate *priv;
858 PurpleAccount *account;
859 PurpleConnection *gc;
860 PurpleProtocol *protocol;
861 GList *ul, *fl;
862 GList *cbuddies = NULL;
864 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
865 g_return_if_fail(users != NULL);
867 priv = purple_chat_conversation_get_instance_private(chat);
868 conv = PURPLE_CONVERSATION(chat);
869 ops = purple_conversation_get_ui_ops(conv);
871 account = purple_conversation_get_account(conv);
872 gc = purple_conversation_get_connection(conv);
873 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
874 protocol = purple_connection_get_protocol(gc);
875 g_return_if_fail(PURPLE_IS_PROTOCOL(protocol));
877 ul = users;
878 fl = flags;
879 while ((ul != NULL) && (fl != NULL)) {
880 const char *user = (const char *)ul->data;
881 const char *alias = user;
882 gboolean quiet;
883 PurpleChatUserFlags flag = GPOINTER_TO_INT(fl->data);
884 const char *extra_msg = (extra_msgs ? extra_msgs->data : NULL);
886 if(!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
887 if (purple_strequal(priv->nick, purple_normalize(account, user))) {
888 const char *alias2 = purple_account_get_private_alias(account);
889 if (alias2 != NULL)
890 alias = alias2;
891 else
893 const char *display_name = purple_connection_get_display_name(gc);
894 if (display_name != NULL)
895 alias = display_name;
897 } else {
898 PurpleBuddy *buddy;
899 if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), user)) != NULL)
900 alias = purple_buddy_get_contact_alias(buddy);
904 quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
905 "chat-user-joining", chat, user, flag)) ||
906 purple_chat_conversation_is_ignored_user(chat, user);
908 chatuser = purple_chat_user_new(chat, user, alias, flag);
910 g_hash_table_replace(priv->users,
911 g_strdup(purple_chat_user_get_name(chatuser)),
912 chatuser);
914 cbuddies = g_list_prepend(cbuddies, chatuser);
916 if (!quiet && new_arrivals) {
917 char *alias_esc = g_markup_escape_text(alias, -1);
918 char *tmp;
920 if (extra_msg == NULL)
921 tmp = g_strdup_printf(_("%s entered the room."), alias_esc);
922 else {
923 char *extra_msg_esc = g_markup_escape_text(extra_msg, -1);
924 tmp = g_strdup_printf(_("%s [<I>%s</I>] entered the room."),
925 alias_esc, extra_msg_esc);
926 g_free(extra_msg_esc);
928 g_free(alias_esc);
930 purple_conversation_write_system_message(
931 conv, tmp, PURPLE_MESSAGE_NO_LINKIFY);
932 g_free(tmp);
935 purple_signal_emit(purple_conversations_get_handle(),
936 "chat-user-joined", chat, user, flag, new_arrivals);
937 ul = ul->next;
938 fl = fl->next;
939 if (extra_msgs != NULL)
940 extra_msgs = extra_msgs->next;
943 cbuddies = g_list_sort(cbuddies, (GCompareFunc)purple_chat_user_compare);
945 if (ops != NULL && ops->chat_add_users != NULL)
946 ops->chat_add_users(chat, cbuddies, new_arrivals);
948 g_list_free(cbuddies);
951 void
952 purple_chat_conversation_rename_user(PurpleChatConversation *chat, const char *old_user,
953 const char *new_user)
955 PurpleConversation *conv;
956 PurpleConversationUiOps *ops;
957 PurpleAccount *account;
958 PurpleConnection *gc;
959 PurpleProtocol *protocol;
960 PurpleChatUser *cb;
961 PurpleChatUserFlags flags;
962 PurpleChatConversationPrivate *priv;
963 const char *new_alias = new_user;
964 char tmp[BUF_LONG];
965 gboolean is_me = FALSE;
967 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
968 g_return_if_fail(old_user != NULL);
969 g_return_if_fail(new_user != NULL);
971 priv = purple_chat_conversation_get_instance_private(chat);
972 conv = PURPLE_CONVERSATION(chat);
973 ops = purple_conversation_get_ui_ops(conv);
974 account = purple_conversation_get_account(conv);
976 gc = purple_conversation_get_connection(conv);
977 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
978 protocol = purple_connection_get_protocol(gc);
979 g_return_if_fail(PURPLE_IS_PROTOCOL(protocol));
981 if (purple_strequal(priv->nick, purple_normalize(account, old_user))) {
982 const char *alias;
984 /* Note this for later. */
985 is_me = TRUE;
987 if(!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
988 alias = purple_account_get_private_alias(account);
989 if (alias != NULL)
990 new_alias = alias;
991 else
993 const char *display_name = purple_connection_get_display_name(gc);
994 if (display_name != NULL)
995 new_alias = display_name;
998 } else if (!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
999 PurpleBuddy *buddy;
1000 if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), new_user)) != NULL)
1001 new_alias = purple_buddy_get_contact_alias(buddy);
1004 flags = purple_chat_user_get_flags(purple_chat_conversation_find_user(chat, old_user));
1005 cb = purple_chat_user_new(chat, new_user, new_alias, flags);
1007 g_hash_table_replace(priv->users,
1008 g_strdup(purple_chat_user_get_name(cb)), cb);
1010 if (ops != NULL && ops->chat_rename_user != NULL)
1011 ops->chat_rename_user(chat, old_user, new_user, new_alias);
1013 cb = purple_chat_conversation_find_user(chat, old_user);
1015 if (cb)
1016 g_hash_table_remove(priv->users, purple_chat_user_get_name(cb));
1018 if (purple_chat_conversation_is_ignored_user(chat, old_user)) {
1019 purple_chat_conversation_unignore(chat, old_user);
1020 purple_chat_conversation_ignore(chat, new_user);
1022 else if (purple_chat_conversation_is_ignored_user(chat, new_user))
1023 purple_chat_conversation_unignore(chat, new_user);
1025 if (is_me)
1026 purple_chat_conversation_set_nick(chat, new_user);
1028 if (purple_prefs_get_bool("/purple/conversations/chat/show_nick_change") &&
1029 !purple_chat_conversation_is_ignored_user(chat, new_user)) {
1031 if (is_me) {
1032 char *escaped = g_markup_escape_text(new_user, -1);
1033 g_snprintf(tmp, sizeof(tmp),
1034 _("You are now known as %s"), escaped);
1035 g_free(escaped);
1036 } else {
1037 const char *old_alias = old_user;
1038 const char *new_alias = new_user;
1039 char *escaped;
1040 char *escaped2;
1042 if (!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
1043 PurpleBuddy *buddy;
1045 if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), old_user)) != NULL)
1046 old_alias = purple_buddy_get_contact_alias(buddy);
1047 if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), new_user)) != NULL)
1048 new_alias = purple_buddy_get_contact_alias(buddy);
1051 escaped = g_markup_escape_text(old_alias, -1);
1052 escaped2 = g_markup_escape_text(new_alias, -1);
1053 g_snprintf(tmp, sizeof(tmp),
1054 _("%s is now known as %s"), escaped, escaped2);
1055 g_free(escaped);
1056 g_free(escaped2);
1059 purple_conversation_write_system_message(conv,
1060 tmp, PURPLE_MESSAGE_NO_LINKIFY);
1064 void
1065 purple_chat_conversation_remove_user(PurpleChatConversation *chat, const char *user, const char *reason)
1067 GList *users = g_list_append(NULL, (char *)user);
1069 purple_chat_conversation_remove_users(chat, users, reason);
1071 g_list_free(users);
1074 void
1075 purple_chat_conversation_remove_users(PurpleChatConversation *chat, GList *users, const char *reason)
1077 PurpleConversation *conv;
1078 PurpleConnection *gc;
1079 PurpleProtocol *protocol;
1080 PurpleConversationUiOps *ops;
1081 PurpleChatUser *cb;
1082 PurpleChatConversationPrivate *priv;
1083 GList *l;
1084 gboolean quiet;
1086 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
1087 g_return_if_fail(users != NULL);
1089 priv = purple_chat_conversation_get_instance_private(chat);
1090 conv = PURPLE_CONVERSATION(chat);
1092 gc = purple_conversation_get_connection(conv);
1093 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
1094 protocol = purple_connection_get_protocol(gc);
1095 g_return_if_fail(PURPLE_IS_PROTOCOL(protocol));
1097 ops = purple_conversation_get_ui_ops(conv);
1099 for (l = users; l != NULL; l = l->next) {
1100 const char *user = (const char *)l->data;
1101 quiet = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_conversations_get_handle(),
1102 "chat-user-leaving", chat, user, reason)) |
1103 purple_chat_conversation_is_ignored_user(chat, user);
1105 cb = purple_chat_conversation_find_user(chat, user);
1107 if (cb) {
1108 g_hash_table_remove(priv->users,
1109 purple_chat_user_get_name(cb));
1112 /* NOTE: Don't remove them from ignored in case they re-enter. */
1114 if (!quiet) {
1115 const char *alias = user;
1116 char *alias_esc;
1117 char *tmp;
1119 if (!(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
1120 PurpleBuddy *buddy;
1122 if ((buddy = purple_blist_find_buddy(purple_connection_get_account(gc), user)) != NULL)
1123 alias = purple_buddy_get_contact_alias(buddy);
1126 alias_esc = g_markup_escape_text(alias, -1);
1128 if (reason == NULL || !*reason)
1129 tmp = g_strdup_printf(_("%s left the room."), alias_esc);
1130 else {
1131 char *reason_esc = g_markup_escape_text(reason, -1);
1132 tmp = g_strdup_printf(_("%s left the room (%s)."),
1133 alias_esc, reason_esc);
1134 g_free(reason_esc);
1136 g_free(alias_esc);
1138 purple_conversation_write_system_message(conv,
1139 tmp, PURPLE_MESSAGE_NO_LINKIFY);
1140 g_free(tmp);
1143 purple_signal_emit(purple_conversations_get_handle(), "chat-user-left",
1144 conv, user, reason);
1147 if (ops != NULL && ops->chat_remove_users != NULL)
1148 ops->chat_remove_users(chat, users);
1151 void
1152 purple_chat_conversation_clear_users(PurpleChatConversation *chat)
1154 PurpleChatConversationPrivate *priv = NULL;
1155 PurpleConversationUiOps *ops;
1156 GHashTableIter it;
1157 gchar *name;
1159 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
1161 priv = purple_chat_conversation_get_instance_private(chat);
1162 ops = purple_conversation_get_ui_ops(PURPLE_CONVERSATION(chat));
1164 if (ops != NULL && ops->chat_remove_users != NULL) {
1165 GList *names = NULL;
1167 g_hash_table_iter_init(&it, priv->users);
1168 while (g_hash_table_iter_next(&it, (gpointer*)&name, NULL))
1169 names = g_list_prepend(names, name);
1171 ops->chat_remove_users(chat, names);
1172 g_list_free(names);
1175 g_hash_table_iter_init(&it, priv->users);
1176 while (g_hash_table_iter_next(&it, (gpointer*)&name, NULL)) {
1177 purple_signal_emit(purple_conversations_get_handle(),
1178 "chat-user-leaving", chat, name, NULL);
1179 purple_signal_emit(purple_conversations_get_handle(),
1180 "chat-user-left", chat, name, NULL);
1183 g_hash_table_remove_all(priv->users);
1186 void purple_chat_conversation_set_nick(PurpleChatConversation *chat, const char *nick) {
1187 PurpleChatConversationPrivate *priv = NULL;
1189 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
1191 priv = purple_chat_conversation_get_instance_private(chat);
1192 g_free(priv->nick);
1193 priv->nick = g_strdup(purple_normalize(
1194 purple_conversation_get_account(PURPLE_CONVERSATION(chat)), nick));
1196 g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_NICK]);
1199 const char *purple_chat_conversation_get_nick(PurpleChatConversation *chat) {
1200 PurpleChatConversationPrivate *priv = NULL;
1202 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
1204 priv = purple_chat_conversation_get_instance_private(chat);
1205 return priv->nick;
1208 static void
1209 invite_user_to_chat(gpointer data, PurpleRequestFields *fields)
1211 PurpleConversation *conv;
1212 PurpleChatConversationPrivate *priv;
1213 const char *user, *message;
1215 conv = data;
1216 priv = purple_chat_conversation_get_instance_private(
1217 PURPLE_CHAT_CONVERSATION(conv));
1218 user = purple_request_fields_get_string(fields, "screenname");
1219 message = purple_request_fields_get_string(fields, "message");
1221 purple_serv_chat_invite(purple_conversation_get_connection(conv), priv->id, message, user);
1224 void purple_chat_conversation_invite_user(PurpleChatConversation *chat, const char *user,
1225 const char *message, gboolean confirm)
1227 PurpleAccount *account;
1228 PurpleRequestFields *fields;
1229 PurpleRequestFieldGroup *group;
1230 PurpleRequestField *field;
1232 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
1234 if (!user || !*user || !message || !*message)
1235 confirm = TRUE;
1237 account = purple_conversation_get_account(PURPLE_CONVERSATION(chat));
1239 if (!confirm) {
1240 purple_serv_chat_invite(purple_account_get_connection(account),
1241 purple_chat_conversation_get_id(chat), message, user);
1242 return;
1245 fields = purple_request_fields_new();
1246 group = purple_request_field_group_new(_("Invite to chat"));
1247 purple_request_fields_add_group(fields, group);
1249 field = purple_request_field_string_new("screenname", _("Buddy"), user, FALSE);
1250 purple_request_field_group_add_field(group, field);
1251 purple_request_field_set_required(field, TRUE);
1252 purple_request_field_set_type_hint(field, "screenname");
1254 field = purple_request_field_string_new("message", _("Message"), message, FALSE);
1255 purple_request_field_group_add_field(group, field);
1257 purple_request_fields(chat, _("Invite to chat"), NULL,
1258 _("Please enter the name of the user you wish to invite, "
1259 "along with an optional invite message."),
1260 fields,
1261 _("Invite"), G_CALLBACK(invite_user_to_chat),
1262 _("Cancel"), NULL,
1263 purple_request_cpar_from_conversation(PURPLE_CONVERSATION(chat)),
1264 chat);
1267 gboolean
1268 purple_chat_conversation_has_user(PurpleChatConversation *chat, const char *user)
1270 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), FALSE);
1271 g_return_val_if_fail(user != NULL, FALSE);
1273 return (purple_chat_conversation_find_user(chat, user) != NULL);
1276 void
1277 purple_chat_conversation_leave(PurpleChatConversation *chat)
1279 PurpleChatConversationPrivate *priv = NULL;
1281 g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
1283 priv = purple_chat_conversation_get_instance_private(chat);
1284 priv->left = TRUE;
1286 if (!g_object_get_data(G_OBJECT(chat), "is-finalizing"))
1287 g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_LEFT]);
1289 purple_conversation_update(PURPLE_CONVERSATION(chat), PURPLE_CONVERSATION_UPDATE_CHATLEFT);
1292 gboolean
1293 purple_chat_conversation_has_left(PurpleChatConversation *chat)
1295 PurpleChatConversationPrivate *priv = NULL;
1297 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), TRUE);
1299 priv = purple_chat_conversation_get_instance_private(chat);
1300 return priv->left;
1303 static void
1304 chat_conversation_cleanup_for_rejoin(PurpleChatConversation *chat)
1306 const char *disp;
1307 PurpleAccount *account;
1308 PurpleConnection *gc;
1309 PurpleConversation *conv = PURPLE_CONVERSATION(chat);
1310 PurpleChatConversationPrivate *priv =
1311 purple_chat_conversation_get_instance_private(chat);
1313 account = purple_conversation_get_account(conv);
1315 purple_conversation_close_logs(conv);
1316 purple_conversation_set_logging(conv, TRUE);
1318 gc = purple_account_get_connection(account);
1320 if ((disp = purple_connection_get_display_name(gc)) != NULL)
1321 purple_chat_conversation_set_nick(chat, disp);
1322 else
1324 purple_chat_conversation_set_nick(chat,
1325 purple_account_get_username(account));
1328 purple_chat_conversation_clear_users(chat);
1329 purple_chat_conversation_set_topic(chat, NULL, NULL);
1330 priv->left = FALSE;
1332 g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_LEFT]);
1334 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_CHATLEFT);
1337 PurpleChatUser *
1338 purple_chat_conversation_find_user(PurpleChatConversation *chat, const char *name)
1340 PurpleChatConversationPrivate *priv = NULL;
1342 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
1343 g_return_val_if_fail(name != NULL, NULL);
1345 priv = purple_chat_conversation_get_instance_private(chat);
1346 return g_hash_table_lookup(priv->users, name);
1349 /**************************************************************************
1350 * GObject code for chats
1351 **************************************************************************/
1353 /* Set method for GObject properties */
1354 static void
1355 purple_chat_conversation_set_property(GObject *obj, guint param_id, const GValue *value,
1356 GParamSpec *pspec)
1358 PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(obj);
1360 switch (param_id) {
1361 case CHAT_PROP_ID:
1362 purple_chat_conversation_set_id(chat, g_value_get_int(value));
1363 break;
1364 case CHAT_PROP_NICK:
1365 purple_chat_conversation_set_nick(chat, g_value_get_string(value));
1366 break;
1367 case CHAT_PROP_LEFT:
1369 gboolean left = g_value_get_boolean(value);
1370 if (left)
1371 purple_chat_conversation_leave(chat);
1373 break;
1374 default:
1375 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
1376 break;
1380 /* Get method for GObject properties */
1381 static void
1382 purple_chat_conversation_get_property(GObject *obj, guint param_id, GValue *value,
1383 GParamSpec *pspec)
1385 PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(obj);
1387 switch (param_id) {
1388 case CHAT_PROP_TOPIC_WHO:
1389 g_value_set_string(value, purple_chat_conversation_get_topic_who(chat));
1390 break;
1391 case CHAT_PROP_TOPIC:
1392 g_value_set_string(value, purple_chat_conversation_get_topic(chat));
1393 break;
1394 case CHAT_PROP_ID:
1395 g_value_set_int(value, purple_chat_conversation_get_id(chat));
1396 break;
1397 case CHAT_PROP_NICK:
1398 g_value_set_string(value, purple_chat_conversation_get_nick(chat));
1399 break;
1400 case CHAT_PROP_LEFT:
1401 g_value_set_boolean(value, purple_chat_conversation_has_left(chat));
1402 break;
1403 default:
1404 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
1405 break;
1409 /* GObject initialization function */
1410 static void purple_chat_conversation_init(PurpleChatConversation *chat)
1412 PurpleChatConversationPrivate *priv =
1413 purple_chat_conversation_get_instance_private(chat);
1415 priv->users = g_hash_table_new_full(_purple_conversation_user_hash,
1416 _purple_conversation_user_equal, g_free, g_object_unref);
1419 /* Called when done constructing */
1420 static void
1421 purple_chat_conversation_constructed(GObject *object)
1423 PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(object);
1424 PurpleAccount *account;
1425 const char *disp;
1427 G_OBJECT_CLASS(purple_chat_conversation_parent_class)->
1428 constructed(object);
1430 g_object_get(object, "account", &account, NULL);
1432 if ((disp = purple_connection_get_display_name(purple_account_get_connection(account))))
1433 purple_chat_conversation_set_nick(chat, disp);
1434 else
1435 purple_chat_conversation_set_nick(chat,
1436 purple_account_get_username(account));
1438 if (purple_prefs_get_bool("/purple/logging/log_chats"))
1439 purple_conversation_set_logging(PURPLE_CONVERSATION(chat), TRUE);
1441 g_object_unref(account);
1444 /* GObject dispose function */
1445 static void
1446 purple_chat_conversation_dispose(GObject *object)
1448 PurpleChatConversationPrivate *priv =
1449 purple_chat_conversation_get_instance_private
1450 (PURPLE_CHAT_CONVERSATION(object));
1452 g_hash_table_remove_all(priv->users);
1454 G_OBJECT_CLASS(purple_chat_conversation_parent_class)->dispose(object);
1457 /* GObject finalize function */
1458 static void
1459 purple_chat_conversation_finalize(GObject *object)
1461 PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(object);
1462 PurpleConnection *gc = purple_conversation_get_connection(PURPLE_CONVERSATION(chat));
1463 PurpleChatConversationPrivate *priv =
1464 purple_chat_conversation_get_instance_private(chat);
1466 if (gc != NULL)
1468 /* Still connected */
1469 int chat_id = purple_chat_conversation_get_id(chat);
1472 * Close the window when the user tells us to, and let the protocol
1473 * deal with the internals on it's own time. Don't do this if the
1474 * protocol already knows it left the chat.
1476 if (!purple_chat_conversation_has_left(chat))
1477 purple_serv_chat_leave(gc, chat_id);
1480 * If they didn't call purple_serv_got_chat_left by now, it's too late.
1481 * So we better do it for them before we destroy the thing.
1483 if (!purple_chat_conversation_has_left(chat))
1484 purple_serv_got_chat_left(gc, chat_id);
1487 g_hash_table_destroy(priv->users);
1488 priv->users = NULL;
1490 g_list_foreach(priv->ignored, (GFunc)g_free, NULL);
1491 g_list_free(priv->ignored);
1492 priv->ignored = NULL;
1494 g_free(priv->who);
1495 g_free(priv->topic);
1496 g_free(priv->nick);
1498 priv->who = NULL;
1499 priv->topic = NULL;
1500 priv->nick = NULL;
1502 G_OBJECT_CLASS(purple_chat_conversation_parent_class)->finalize(object);
1505 /* Class initializer function */
1506 static void purple_chat_conversation_class_init(PurpleChatConversationClass *klass)
1508 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
1509 PurpleConversationClass *conv_class = PURPLE_CONVERSATION_CLASS(klass);
1511 obj_class->dispose = purple_chat_conversation_dispose;
1512 obj_class->finalize = purple_chat_conversation_finalize;
1513 obj_class->constructed = purple_chat_conversation_constructed;
1515 /* Setup properties */
1516 obj_class->get_property = purple_chat_conversation_get_property;
1517 obj_class->set_property = purple_chat_conversation_set_property;
1519 conv_class->write_message = chat_conversation_write_message;
1521 chat_properties[CHAT_PROP_TOPIC_WHO] = g_param_spec_string("topic-who",
1522 "Who set topic",
1523 "Who set the chat topic.", NULL,
1524 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1526 chat_properties[CHAT_PROP_TOPIC] = g_param_spec_string("topic",
1527 "Topic",
1528 "Topic of the chat.", NULL,
1529 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
1531 chat_properties[CHAT_PROP_ID] = g_param_spec_int("chat-id",
1532 "Chat ID",
1533 "The ID of the chat.", G_MININT, G_MAXINT, 0,
1534 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1536 chat_properties[CHAT_PROP_NICK] = g_param_spec_string("nick",
1537 "Nickname",
1538 "The nickname of the user in a chat.", NULL,
1539 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1541 chat_properties[CHAT_PROP_LEFT] = g_param_spec_boolean("left",
1542 "Left the chat",
1543 "Whether the user has left the chat.", FALSE,
1544 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1546 g_object_class_install_properties(obj_class, CHAT_PROP_LAST,
1547 chat_properties);
1550 PurpleChatConversation *
1551 purple_chat_conversation_new(PurpleAccount *account, const char *name)
1553 PurpleChatConversation *chat;
1554 PurpleConnection *gc;
1556 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
1557 g_return_val_if_fail(name != NULL, NULL);
1559 /* Check if this conversation already exists. */
1560 if ((chat = purple_conversations_find_chat_with_account(name, account)) != NULL)
1562 if (!purple_chat_conversation_has_left(chat)) {
1563 purple_debug_warning("conversationtypes", "Trying to create "
1564 "multiple chats (%s) with the same name is deprecated and "
1565 "will be removed in libpurple 3.0.0", name);
1566 } else {
1568 * This hack is necessary because some protocols (MSN) have unnamed chats
1569 * that all use the same name. A PurpleConversation for one of those
1570 * is only ever re-used if the user has left, so calls to
1571 * purple_conversation_new need to fall-through to creating a new
1572 * chat.
1573 * TODO 3.0.0: Remove this workaround and mandate unique names.
1576 chat_conversation_cleanup_for_rejoin(chat);
1577 return chat;
1581 gc = purple_account_get_connection(account);
1582 g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
1584 chat = g_object_new(PURPLE_TYPE_CHAT_CONVERSATION,
1585 "account", account,
1586 "name", name,
1587 "title", name,
1588 NULL);
1590 return chat;
1593 /**************************************************************************
1594 * Chat Conversation User API
1595 **************************************************************************/
1596 static int
1597 purple_chat_user_compare(PurpleChatUser *a, PurpleChatUser *b)
1599 PurpleChatUserFlags f1 = 0, f2 = 0;
1600 PurpleChatUserPrivate *priva, *privb;
1601 char *user1 = NULL, *user2 = NULL;
1602 gint ret = 0;
1604 priva = purple_chat_user_get_instance_private(a);
1605 privb = purple_chat_user_get_instance_private(b);
1607 if (priva) {
1608 f1 = priva->flags;
1609 if (priva->alias_key)
1610 user1 = priva->alias_key;
1611 else if (priva->name)
1612 user1 = priva->name;
1615 if (privb) {
1616 f2 = privb->flags;
1617 if (privb->alias_key)
1618 user2 = privb->alias_key;
1619 else if (privb->name)
1620 user2 = privb->name;
1623 if (user1 == NULL || user2 == NULL) {
1624 if (!(user1 == NULL && user2 == NULL))
1625 ret = (user1 == NULL) ? -1: 1;
1626 } else if (f1 != f2) {
1627 /* sort more important users first */
1628 ret = (f1 > f2) ? -1 : 1;
1629 } else if (priva->buddy != privb->buddy) {
1630 ret = priva->buddy ? -1 : 1;
1631 } else {
1632 ret = purple_utf8_strcasecmp(user1, user2);
1635 return ret;
1638 const char *
1639 purple_chat_user_get_alias(PurpleChatUser *cb)
1641 PurpleChatUserPrivate *priv;
1643 g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), NULL);
1645 priv = purple_chat_user_get_instance_private(cb);
1646 return priv->alias;
1649 const char *
1650 purple_chat_user_get_name(PurpleChatUser *cb)
1652 PurpleChatUserPrivate *priv;
1654 g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), NULL);
1656 priv = purple_chat_user_get_instance_private(cb);
1657 return priv->name;
1660 void
1661 purple_chat_user_set_flags(PurpleChatUser *cb,
1662 PurpleChatUserFlags flags)
1664 PurpleConversationUiOps *ops;
1665 PurpleChatUserFlags oldflags;
1666 PurpleChatUserPrivate *priv;
1668 g_return_if_fail(PURPLE_IS_CHAT_USER(cb));
1670 priv = purple_chat_user_get_instance_private(cb);
1672 if (flags == priv->flags)
1673 return;
1675 oldflags = priv->flags;
1676 priv->flags = flags;
1678 g_object_notify_by_pspec(G_OBJECT(cb), cu_properties[CU_PROP_FLAGS]);
1680 ops = purple_conversation_get_ui_ops(PURPLE_CONVERSATION(priv->chat));
1682 if (ops != NULL && ops->chat_update_user != NULL)
1683 ops->chat_update_user(cb);
1685 purple_signal_emit(purple_conversations_get_handle(),
1686 "chat-user-flags", cb, oldflags, flags);
1689 PurpleChatUserFlags
1690 purple_chat_user_get_flags(PurpleChatUser *cb)
1692 PurpleChatUserPrivate *priv;
1694 g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), PURPLE_CHAT_USER_NONE);
1696 priv = purple_chat_user_get_instance_private(cb);
1697 return priv->flags;
1700 void
1701 purple_chat_user_set_ui_data(PurpleChatUser *cb, gpointer ui_data)
1703 g_return_if_fail(PURPLE_IS_CHAT_USER(cb));
1705 cb->ui_data = ui_data;
1708 gpointer
1709 purple_chat_user_get_ui_data(PurpleChatUser *cb)
1711 g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), NULL);
1713 return cb->ui_data;
1716 void
1717 purple_chat_user_set_chat(PurpleChatUser *cb,
1718 PurpleChatConversation *chat)
1720 PurpleChatUserPrivate *priv;
1722 g_return_if_fail(PURPLE_IS_CHAT_USER(cb));
1724 priv = purple_chat_user_get_instance_private(cb);
1725 priv->chat = chat;
1727 g_object_notify_by_pspec(G_OBJECT(cb), cu_properties[CU_PROP_CHAT]);
1730 PurpleChatConversation *
1731 purple_chat_user_get_chat(PurpleChatUser *cb)
1733 PurpleChatUserPrivate *priv;
1735 g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), NULL);
1737 priv = purple_chat_user_get_instance_private(cb);
1738 return priv->chat;
1741 gboolean
1742 purple_chat_user_is_buddy(PurpleChatUser *cb)
1744 PurpleChatUserPrivate *priv;
1746 g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), FALSE);
1748 priv = purple_chat_user_get_instance_private(cb);
1749 return priv->buddy;
1752 /**************************************************************************
1753 * GObject code for chat user
1754 **************************************************************************/
1756 /* Set method for GObject properties */
1757 static void
1758 purple_chat_user_set_property(GObject *obj, guint param_id, const GValue *value,
1759 GParamSpec *pspec)
1761 PurpleChatUser *cb = PURPLE_CHAT_USER(obj);
1762 PurpleChatUserPrivate *priv =
1763 purple_chat_user_get_instance_private(cb);
1765 switch (param_id) {
1766 case CU_PROP_CHAT:
1767 priv->chat = g_value_get_object(value);
1768 break;
1769 case CU_PROP_NAME:
1770 g_free(priv->name);
1771 priv->name = g_value_dup_string(value);
1772 break;
1773 case CU_PROP_ALIAS:
1774 g_free(priv->alias);
1775 priv->alias = g_value_dup_string(value);
1776 break;
1777 case CU_PROP_FLAGS:
1778 priv->flags = g_value_get_flags(value);
1779 break;
1780 default:
1781 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
1782 break;
1786 /* Get method for GObject properties */
1787 static void
1788 purple_chat_user_get_property(GObject *obj, guint param_id, GValue *value,
1789 GParamSpec *pspec)
1791 PurpleChatUser *cb = PURPLE_CHAT_USER(obj);
1793 switch (param_id) {
1794 case CU_PROP_CHAT:
1795 g_value_set_object(value, purple_chat_user_get_chat(cb));
1796 break;
1797 case CU_PROP_NAME:
1798 g_value_set_string(value, purple_chat_user_get_name(cb));
1799 break;
1800 case CU_PROP_ALIAS:
1801 g_value_set_string(value, purple_chat_user_get_alias(cb));
1802 break;
1803 case CU_PROP_FLAGS:
1804 g_value_set_flags(value, purple_chat_user_get_flags(cb));
1805 break;
1806 default:
1807 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
1808 break;
1812 /* GObject initialization function */
1813 static void
1814 purple_chat_user_init(PurpleChatUser *user)
1818 /* Called when done constructing */
1819 static void
1820 purple_chat_user_constructed(GObject *object)
1822 PurpleChatUserPrivate *priv = purple_chat_user_get_instance_private(
1823 PURPLE_CHAT_USER(object));
1824 PurpleAccount *account;
1826 G_OBJECT_CLASS(purple_chat_user_parent_class)->constructed(object);
1828 account = purple_conversation_get_account(PURPLE_CONVERSATION(priv->chat));
1830 if (purple_blist_find_buddy(account, priv->name) != NULL)
1831 priv->buddy = TRUE;
1834 /* GObject finalize function */
1835 static void
1836 purple_chat_user_finalize(GObject *object)
1838 PurpleChatUser *cb = PURPLE_CHAT_USER(object);
1839 PurpleChatUserPrivate *priv =
1840 purple_chat_user_get_instance_private(cb);
1842 purple_signal_emit(purple_conversations_get_handle(),
1843 "deleting-chat-user", cb);
1845 g_free(priv->alias);
1846 g_free(priv->alias_key);
1847 g_free(priv->name);
1849 G_OBJECT_CLASS(purple_chat_user_parent_class)->finalize(object);
1852 /* Class initializer function */
1853 static void purple_chat_user_class_init(PurpleChatUserClass *klass)
1855 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
1857 obj_class->constructed = purple_chat_user_constructed;
1858 obj_class->finalize = purple_chat_user_finalize;
1860 /* Setup properties */
1861 obj_class->get_property = purple_chat_user_get_property;
1862 obj_class->set_property = purple_chat_user_set_property;
1864 cu_properties[CU_PROP_CHAT] = g_param_spec_object("chat", "Chat",
1865 "The chat the buddy belongs to.", PURPLE_TYPE_CHAT_CONVERSATION,
1866 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
1867 G_PARAM_STATIC_STRINGS);
1869 cu_properties[CU_PROP_NAME] = g_param_spec_string("name", "Name",
1870 "Name of the chat user.", NULL,
1871 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1873 cu_properties[CU_PROP_ALIAS] = g_param_spec_string("alias", "Alias",
1874 "Alias of the chat user.", NULL,
1875 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1877 cu_properties[CU_PROP_FLAGS] = g_param_spec_flags("flags", "Buddy flags",
1878 "The flags for the chat user.",
1879 PURPLE_TYPE_CHAT_USER_FLAGS, PURPLE_CHAT_USER_NONE,
1880 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1882 g_object_class_install_properties(obj_class, CU_PROP_LAST, cu_properties);
1885 PurpleChatUser *
1886 purple_chat_user_new(PurpleChatConversation *chat, const char *name,
1887 const char *alias, PurpleChatUserFlags flags)
1889 PurpleChatUser *cb;
1891 g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
1892 g_return_val_if_fail(name != NULL, NULL);
1894 cb = g_object_new(PURPLE_TYPE_CHAT_USER,
1895 "chat", chat,
1896 "name", name,
1897 "alias", alias,
1898 "flags", flags,
1899 NULL);
1901 return cb;