Standardize all protocol header guard macros.
[pidgin-git.git] / libpurple / conversation.c
blob953d703ab9cf8e63d3f3be9198d40c2b92056412
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"
25 #include "buddylist.h"
26 #include "cmds.h"
27 #include "conversation.h"
28 #include "debug.h"
29 #include "enums.h"
30 #include "notify.h"
31 #include "prefs.h"
32 #include "protocol.h"
33 #include "request.h"
34 #include "signals.h"
35 #include "smiley-list.h"
36 #include "util.h"
38 typedef struct _PurpleConversationPrivate PurpleConversationPrivate;
40 /* General private data for a conversation */
41 struct _PurpleConversationPrivate
43 PurpleAccount *account; /* The user using this conversation. */
45 char *name; /* The name of the conversation. */
46 char *title; /* The window title. */
48 gboolean logging; /* The status of logging. */
50 GList *logs; /* This conversation's logs */
52 PurpleConversationUiOps *ui_ops; /* UI-specific operations. */
54 PurpleConnectionFlags features; /* The supported features */
55 GList *message_history; /* Message history, as a GList of PurpleMessages */
57 PurpleE2eeState *e2ee_state; /* End-to-end encryption state. */
59 /* The list of remote smileys. This should be per-buddy (PurpleBuddy),
60 * but we don't have any class for people not on our buddy
61 * list (PurpleDude?). So, if we have one, we should switch to it. */
62 PurpleSmileyList *remote_smileys;
65 /* GObject Property enums */
66 enum
68 PROP_0,
69 PROP_ACCOUNT,
70 PROP_NAME,
71 PROP_TITLE,
72 PROP_LOGGING,
73 PROP_FEATURES,
74 PROP_LAST
77 static GParamSpec *properties[PROP_LAST];
79 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(PurpleConversation, purple_conversation,
80 G_TYPE_OBJECT);
82 static void
83 common_send(PurpleConversation *conv, const char *message, PurpleMessageFlags msgflags)
85 PurpleAccount *account;
86 PurpleConnection *gc;
87 PurpleConversationPrivate *priv =
88 purple_conversation_get_instance_private(conv);
89 PurpleMessage *msg;
90 char *displayed = NULL;
91 const char *sent;
92 int err = 0;
94 if (*message == '\0')
95 return;
97 account = purple_conversation_get_account(conv);
98 g_return_if_fail(PURPLE_IS_ACCOUNT(account));
100 gc = purple_account_get_connection(account);
101 g_return_if_fail(PURPLE_IS_CONNECTION(gc));
103 /* Always linkfy the text for display, unless we're
104 * explicitly asked to do otheriwse*/
105 if (!(msgflags & PURPLE_MESSAGE_INVISIBLE)) {
106 if(msgflags & PURPLE_MESSAGE_NO_LINKIFY)
107 displayed = g_strdup(message);
108 else
109 displayed = purple_markup_linkify(message);
112 if (displayed && (priv->features & PURPLE_CONNECTION_FLAG_HTML) &&
113 !(msgflags & PURPLE_MESSAGE_RAW)) {
114 sent = displayed;
115 } else
116 sent = message;
118 msgflags |= PURPLE_MESSAGE_SEND;
120 if (PURPLE_IS_IM_CONVERSATION(conv)) {
121 msg = purple_message_new_outgoing(
122 purple_conversation_get_name(conv), sent, msgflags);
124 purple_signal_emit(purple_conversations_get_handle(), "sending-im-msg",
125 account, msg);
127 if (!purple_message_is_empty(msg)) {
129 err = purple_serv_send_im(gc, msg);
131 if ((err > 0) && (displayed != NULL)) {
132 /* revert the contents in case sending-im-msg changed it */
133 purple_message_set_contents(msg, displayed);
134 purple_conversation_write_message(conv, msg);
137 purple_signal_emit(purple_conversations_get_handle(),
138 "sent-im-msg", account, msg);
141 else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
142 int id = purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(conv));
144 msg = purple_message_new_outgoing(NULL, sent, msgflags);
146 purple_signal_emit(purple_conversations_get_handle(),
147 "sending-chat-msg", account, msg, id);
149 if (!purple_message_is_empty(msg)) {
150 err = purple_serv_chat_send(gc, id, msg);
152 purple_signal_emit(purple_conversations_get_handle(),
153 "sent-chat-msg", account, msg, id);
157 if (err < 0) {
158 const char *who;
159 const char *msg;
161 who = purple_conversation_get_name(conv);
163 if (err == -E2BIG) {
164 msg = _("Unable to send message: The message is too large.");
166 if (!purple_conversation_present_error(who, account, msg)) {
167 char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who);
168 purple_notify_error(gc, NULL, msg2,
169 _("The message is too large."),
170 purple_request_cpar_from_connection(gc));
171 g_free(msg2);
174 else if (err == -ENOTCONN) {
175 purple_debug(PURPLE_DEBUG_ERROR, "conversation",
176 "Not yet connected.\n");
178 else {
179 msg = _("Unable to send message.");
181 if (!purple_conversation_present_error(who, account, msg)) {
182 char *msg2 = g_strdup_printf(_("Unable to send message to %s."), who);
183 purple_notify_error(gc, NULL, msg2, NULL,
184 purple_request_cpar_from_connection(gc));
185 g_free(msg2);
190 g_free(displayed);
193 /**************************************************************************
194 * Conversation API
195 **************************************************************************/
196 void
197 purple_conversation_present(PurpleConversation *conv) {
198 PurpleConversationUiOps *ops;
200 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
202 ops = purple_conversation_get_ui_ops(conv);
203 if(ops && ops->present)
204 ops->present(conv);
207 void
208 purple_conversation_set_features(PurpleConversation *conv, PurpleConnectionFlags features)
210 PurpleConversationPrivate *priv = NULL;
212 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
214 priv = purple_conversation_get_instance_private(conv);
215 priv->features = features;
217 g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_FEATURES]);
219 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_FEATURES);
222 PurpleConnectionFlags
223 purple_conversation_get_features(PurpleConversation *conv)
225 PurpleConversationPrivate *priv = NULL;
227 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), 0);
229 priv = purple_conversation_get_instance_private(conv);
230 return priv->features;
233 static PurpleConversationUiOps *
234 purple_conversation_ui_ops_copy(PurpleConversationUiOps *ops)
236 PurpleConversationUiOps *ops_new;
238 g_return_val_if_fail(ops != NULL, NULL);
240 ops_new = g_new(PurpleConversationUiOps, 1);
241 *ops_new = *ops;
243 return ops_new;
246 GType
247 purple_conversation_ui_ops_get_type(void)
249 static GType type = 0;
251 if (type == 0) {
252 type = g_boxed_type_register_static("PurpleConversationUiOps",
253 (GBoxedCopyFunc)purple_conversation_ui_ops_copy,
254 (GBoxedFreeFunc)g_free);
257 return type;
260 void
261 purple_conversation_set_ui_ops(PurpleConversation *conv,
262 PurpleConversationUiOps *ops)
264 PurpleConversationPrivate *priv = NULL;
266 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
267 priv = purple_conversation_get_instance_private(conv);
269 if (priv->ui_ops == ops)
270 return;
272 if (priv->ui_ops != NULL && priv->ui_ops->destroy_conversation != NULL)
273 priv->ui_ops->destroy_conversation(conv);
275 priv->ui_ops = ops;
278 PurpleConversationUiOps *
279 purple_conversation_get_ui_ops(PurpleConversation *conv)
281 PurpleConversationPrivate *priv = NULL;
283 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
285 priv = purple_conversation_get_instance_private(conv);
286 return priv->ui_ops;
289 void
290 purple_conversation_set_account(PurpleConversation *conv, PurpleAccount *account)
292 PurpleConversationPrivate *priv = NULL;
294 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
295 priv = purple_conversation_get_instance_private(conv);
297 if (account == purple_conversation_get_account(conv))
298 return;
300 _purple_conversations_update_cache(conv, NULL, account);
301 priv->account = account;
303 g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_ACCOUNT]);
305 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_ACCOUNT);
308 PurpleAccount *
309 purple_conversation_get_account(PurpleConversation *conv)
311 PurpleConversationPrivate *priv = NULL;
313 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
315 priv = purple_conversation_get_instance_private(conv);
316 return priv->account;
319 PurpleConnection *
320 purple_conversation_get_connection(PurpleConversation *conv)
322 PurpleAccount *account;
324 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
326 account = purple_conversation_get_account(conv);
328 if (account == NULL)
329 return NULL;
331 return purple_account_get_connection(account);
334 void
335 purple_conversation_set_title(PurpleConversation *conv, const char *title)
337 PurpleConversationPrivate *priv = NULL;
339 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
340 g_return_if_fail(title != NULL);
342 priv = purple_conversation_get_instance_private(conv);
343 g_free(priv->title);
344 priv->title = g_strdup(title);
346 if (!g_object_get_data(G_OBJECT(conv), "is-finalizing"))
347 g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_TITLE]);
349 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_TITLE);
352 const char *
353 purple_conversation_get_title(PurpleConversation *conv)
355 PurpleConversationPrivate *priv = NULL;
357 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
359 priv = purple_conversation_get_instance_private(conv);
360 return priv->title;
363 void
364 purple_conversation_autoset_title(PurpleConversation *conv)
366 PurpleAccount *account;
367 PurpleBuddy *b;
368 PurpleChat *chat;
369 const char *text = NULL, *name;
371 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
373 account = purple_conversation_get_account(conv);
374 name = purple_conversation_get_name(conv);
376 if (PURPLE_IS_IM_CONVERSATION(conv)) {
377 if (account && ((b = purple_blist_find_buddy(account, name)) != NULL))
378 text = purple_buddy_get_contact_alias(b);
379 } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
380 if (account && ((chat = purple_blist_find_chat(account, name)) != NULL))
381 text = purple_chat_get_name(chat);
384 if(text == NULL)
385 text = name;
387 purple_conversation_set_title(conv, text);
390 void
391 purple_conversation_set_name(PurpleConversation *conv, const char *name)
393 PurpleConversationPrivate *priv = NULL;
395 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
396 priv = purple_conversation_get_instance_private(conv);
398 _purple_conversations_update_cache(conv, name, NULL);
400 g_free(priv->name);
401 priv->name = g_strdup(name);
403 g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_NAME]);
405 purple_conversation_autoset_title(conv);
406 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_NAME);
409 const char *
410 purple_conversation_get_name(PurpleConversation *conv)
412 PurpleConversationPrivate *priv = NULL;
414 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
416 priv = purple_conversation_get_instance_private(conv);
417 return priv->name;
420 void
421 purple_conversation_set_e2ee_state(PurpleConversation *conv,
422 PurpleE2eeState *state)
424 PurpleConversationPrivate *priv = NULL;
426 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
427 priv = purple_conversation_get_instance_private(conv);
429 if (state != NULL && purple_e2ee_state_get_provider(state) !=
430 purple_e2ee_provider_get_main())
432 purple_debug_error("conversation",
433 "This is not the main e2ee provider");
435 return;
438 if (state == priv->e2ee_state)
439 return;
441 if (state)
442 purple_e2ee_state_ref(state);
443 purple_e2ee_state_unref(priv->e2ee_state);
444 priv->e2ee_state = state;
446 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_E2EE);
449 PurpleE2eeState *
450 purple_conversation_get_e2ee_state(PurpleConversation *conv)
452 PurpleConversationPrivate *priv = NULL;
453 PurpleE2eeProvider *provider;
455 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
456 priv = purple_conversation_get_instance_private(conv);
458 if (priv->e2ee_state == NULL)
459 return NULL;
461 provider = purple_e2ee_provider_get_main();
462 if (provider == NULL)
463 return NULL;
465 if (purple_e2ee_state_get_provider(priv->e2ee_state) != provider) {
466 purple_debug_warning("conversation",
467 "e2ee state has invalid provider set");
468 return NULL;
471 return priv->e2ee_state;
474 void
475 purple_conversation_set_logging(PurpleConversation *conv, gboolean log)
477 PurpleConversationPrivate *priv = NULL;
479 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
480 priv = purple_conversation_get_instance_private(conv);
482 if (priv->logging != log)
484 priv->logging = log;
485 if (log && priv->logs == NULL) {
486 GDateTime *dt;
487 PurpleLog *log;
489 dt = g_date_time_new_now_local();
490 log = purple_log_new(PURPLE_IS_CHAT_CONVERSATION(conv)
491 ? PURPLE_LOG_CHAT
492 : PURPLE_LOG_IM,
493 priv->name, priv->account, conv,
494 dt);
495 g_date_time_unref(dt);
497 priv->logs = g_list_append(NULL, log);
500 g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_LOGGING]);
502 purple_conversation_update(conv, PURPLE_CONVERSATION_UPDATE_LOGGING);
506 gboolean
507 purple_conversation_is_logging(PurpleConversation *conv)
509 PurpleConversationPrivate *priv = NULL;
511 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), FALSE);
513 priv = purple_conversation_get_instance_private(conv);
514 return priv->logging;
517 void
518 purple_conversation_close_logs(PurpleConversation *conv)
520 PurpleConversationPrivate *priv = NULL;
522 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
524 priv = purple_conversation_get_instance_private(conv);
525 g_list_foreach(priv->logs, (GFunc)purple_log_free, NULL);
526 g_list_free(priv->logs);
527 priv->logs = NULL;
530 void
531 _purple_conversation_write_common(PurpleConversation *conv, PurpleMessage *pmsg)
533 PurpleConversationPrivate *priv = NULL;
534 PurpleProtocol *protocol = NULL;
535 PurpleConnection *gc = NULL;
536 PurpleAccount *account;
537 PurpleConversationUiOps *ops;
538 PurpleBuddy *b;
539 int plugin_return;
540 /* int logging_font_options = 0; */
542 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
543 g_return_if_fail(pmsg != NULL);
545 priv = purple_conversation_get_instance_private(conv);
546 ops = purple_conversation_get_ui_ops(conv);
548 account = purple_conversation_get_account(conv);
550 if (account != NULL)
551 gc = purple_account_get_connection(account);
553 if (PURPLE_IS_CHAT_CONVERSATION(conv) &&
554 (gc != NULL && !g_slist_find(purple_connection_get_active_chats(gc), conv)))
555 return;
557 if (PURPLE_IS_IM_CONVERSATION(conv) &&
558 !g_list_find(purple_conversations_get_all(), conv))
559 return;
561 plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
562 purple_conversations_get_handle(),
563 (PURPLE_IS_IM_CONVERSATION(conv) ? "writing-im-msg" : "writing-chat-msg"),
564 conv, pmsg));
566 if (purple_message_is_empty(pmsg))
567 return;
569 if (plugin_return)
570 return;
572 if (account != NULL) {
573 protocol = purple_protocols_find(purple_account_get_protocol_id(account));
575 if (PURPLE_IS_IM_CONVERSATION(conv) ||
576 !(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME)) {
578 if (purple_message_get_flags(pmsg) & PURPLE_MESSAGE_SEND) {
579 const gchar *alias;
581 b = purple_blist_find_buddy(account,
582 purple_account_get_username(account));
584 if (purple_account_get_private_alias(account) != NULL)
585 alias = purple_account_get_private_alias(account);
586 else if (b != NULL && !purple_strequal(purple_buddy_get_name(b),
587 purple_buddy_get_contact_alias(b)))
589 alias = purple_buddy_get_contact_alias(b);
590 } else if (purple_connection_get_display_name(gc) != NULL)
591 alias = purple_connection_get_display_name(gc);
592 else
593 alias = purple_account_get_username(account);
595 purple_message_set_author_alias(pmsg, alias);
597 else if (purple_message_get_flags(pmsg) & PURPLE_MESSAGE_RECV)
599 /* TODO: PurpleDude - folks not on the buddy list */
600 b = purple_blist_find_buddy(account,
601 purple_message_get_author(pmsg));
603 if (b != NULL) {
604 purple_message_set_author_alias(pmsg,
605 purple_buddy_get_contact_alias(b));
611 if (!(purple_message_get_flags(pmsg) & PURPLE_MESSAGE_NO_LOG) && purple_conversation_is_logging(conv)) {
612 GList *log;
613 GDateTime *dt;
615 dt = g_date_time_new_from_unix_local(purple_message_get_time(pmsg));
616 log = priv->logs;
617 while (log != NULL) {
618 purple_log_write((PurpleLog *)log->data,
619 purple_message_get_flags(pmsg),
620 purple_message_get_author_alias(pmsg),
622 purple_message_get_contents(pmsg));
623 log = log->next;
625 g_date_time_unref(dt);
628 if (ops) {
629 if (PURPLE_IS_CHAT_CONVERSATION(conv) && ops->write_chat)
630 ops->write_chat(PURPLE_CHAT_CONVERSATION(conv), pmsg);
631 else if (PURPLE_IS_IM_CONVERSATION(conv) && ops->write_im)
632 ops->write_im(PURPLE_IM_CONVERSATION(conv), pmsg);
633 else if (ops->write_conv)
634 ops->write_conv(conv, pmsg);
637 g_object_ref(pmsg);
638 priv->message_history = g_list_prepend(priv->message_history, pmsg);
640 purple_signal_emit(purple_conversations_get_handle(),
641 (PURPLE_IS_IM_CONVERSATION(conv) ? "wrote-im-msg" : "wrote-chat-msg"),
642 conv, pmsg);
645 void
646 purple_conversation_write_message(PurpleConversation *conv, PurpleMessage *msg)
648 PurpleConversationClass *klass = NULL;
650 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
652 klass = PURPLE_CONVERSATION_GET_CLASS(conv);
654 if (klass && klass->write_message)
655 klass->write_message(conv, msg);
658 void purple_conversation_write_system_message(PurpleConversation *conv,
659 const gchar *message, PurpleMessageFlags flags)
661 _purple_conversation_write_common(conv,
662 purple_message_new_system(message, flags));
665 void
666 purple_conversation_send(PurpleConversation *conv, const char *message)
668 purple_conversation_send_with_flags(conv, message, 0);
671 void
672 purple_conversation_send_with_flags(PurpleConversation *conv, const char *message,
673 PurpleMessageFlags flags)
675 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
676 g_return_if_fail(message != NULL);
678 common_send(conv, message, flags);
681 gboolean
682 purple_conversation_has_focus(PurpleConversation *conv)
684 gboolean ret = FALSE;
685 PurpleConversationUiOps *ops;
687 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), FALSE);
689 ops = purple_conversation_get_ui_ops(conv);
691 if (ops != NULL && ops->has_focus != NULL)
692 ret = ops->has_focus(conv);
694 return ret;
698 * TODO: Need to make sure calls to this function happen in the core
699 * instead of the UI. That way UIs have less work to do, and the
700 * core/UI split is cleaner. Also need to make sure this is called
701 * when chats are added/removed from the blist.
703 void
704 purple_conversation_update(PurpleConversation *conv, PurpleConversationUpdateType type)
706 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
708 purple_signal_emit(purple_conversations_get_handle(),
709 "conversation-updated", conv, type);
712 gboolean purple_conversation_present_error(const char *who, PurpleAccount *account, const char *what)
714 PurpleConversation *conv;
716 g_return_val_if_fail(who != NULL, FALSE);
717 g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), FALSE);
718 g_return_val_if_fail(what != NULL, FALSE);
720 conv = purple_conversations_find_with_account(who, account);
721 if (conv != NULL)
722 purple_conversation_write_system_message(conv, what, PURPLE_MESSAGE_ERROR);
723 else
724 return FALSE;
726 return TRUE;
729 static void
730 purple_conversation_send_confirm_cb(gpointer *data)
732 PurpleConversation *conv = data[0];
733 char *message = data[1];
735 g_free(data);
737 if (!PURPLE_IS_CONVERSATION(conv)) {
738 /* Maybe it was closed before this callback was called. */
739 return;
742 common_send(conv, message, 0);
745 void
746 purple_conversation_send_confirm(PurpleConversation *conv, const char *message)
748 PurpleConversationPrivate *priv = NULL;
749 char *text;
750 gpointer *data;
752 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
753 g_return_if_fail(message != NULL);
755 priv = purple_conversation_get_instance_private(conv);
756 if (priv->ui_ops != NULL && priv->ui_ops->send_confirm != NULL)
758 priv->ui_ops->send_confirm(conv, message);
759 return;
762 text = g_strdup_printf("You are about to send the following message:\n%s", message);
763 data = g_new0(gpointer, 2);
764 data[0] = conv;
765 data[1] = (gpointer)message;
767 purple_request_action(conv, NULL, _("Send Message"), text, 0,
768 purple_request_cpar_from_account(
769 purple_conversation_get_account(conv)),
770 data, 2, _("_Send Message"),
771 G_CALLBACK(purple_conversation_send_confirm_cb), _("Cancel"), NULL);
774 GList *
775 purple_conversation_get_extended_menu(PurpleConversation *conv)
777 GList *menu = NULL;
779 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
781 purple_signal_emit(purple_conversations_get_handle(),
782 "conversation-extended-menu", conv, &menu);
783 return menu;
786 void purple_conversation_clear_message_history(PurpleConversation *conv)
788 PurpleConversationPrivate *priv = NULL;
789 GList *list;
791 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
793 priv = purple_conversation_get_instance_private(conv);
794 list = priv->message_history;
795 g_list_free_full(list, g_object_unref);
796 priv->message_history = NULL;
798 purple_signal_emit(purple_conversations_get_handle(),
799 "cleared-message-history", conv);
802 GList *purple_conversation_get_message_history(PurpleConversation *conv)
804 PurpleConversationPrivate *priv = NULL;
806 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
808 priv = purple_conversation_get_instance_private(conv);
809 return priv->message_history;
812 void purple_conversation_set_ui_data(PurpleConversation *conv, gpointer ui_data)
814 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
816 conv->ui_data = ui_data;
819 gpointer purple_conversation_get_ui_data(const PurpleConversation *conv)
821 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
823 return conv->ui_data;
826 gboolean
827 purple_conversation_do_command(PurpleConversation *conv, const gchar *cmdline,
828 const gchar *markup, gchar **error)
830 char *mark = (markup && *markup) ? NULL : g_markup_escape_text(cmdline, -1), *err = NULL;
831 PurpleCmdStatus status = purple_cmd_do_command(conv, cmdline, mark ? mark : markup, error ? error : &err);
832 g_free(mark);
833 g_free(err);
834 return (status == PURPLE_CMD_STATUS_OK);
837 gssize
838 purple_conversation_get_max_message_size(PurpleConversation *conv)
840 PurpleProtocol *protocol;
842 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), 0);
844 protocol = purple_connection_get_protocol(
845 purple_conversation_get_connection(conv));
847 g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), 0);
849 return purple_protocol_client_iface_get_max_message_size(protocol, conv);
852 void
853 purple_conversation_add_smiley(PurpleConversation *conv, PurpleSmiley *smiley) {
854 PurpleConversationPrivate *priv = NULL;
856 g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
857 g_return_if_fail(smiley);
859 priv = purple_conversation_get_instance_private(conv);
861 if (priv->remote_smileys == NULL) {
862 priv->remote_smileys = purple_smiley_list_new();
863 g_object_set(priv->remote_smileys,
864 "drop-failed-remotes", TRUE, NULL);
867 if(purple_smiley_list_get_by_shortcut(
868 priv->remote_smileys,
869 purple_smiley_get_shortcut(smiley)))
871 /* smiley was already added */
872 return;
875 if (!purple_smiley_list_add(priv->remote_smileys, smiley)) {
876 purple_debug_error("conversation", "failed adding remote "
877 "smiley to the list");
881 PurpleSmiley *
882 purple_conversation_get_smiley(PurpleConversation *conv, const gchar *shortcut) {
883 PurpleConversationPrivate *priv = NULL;
885 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
886 g_return_val_if_fail(shortcut, NULL);
888 priv = purple_conversation_get_instance_private(conv);
890 if (priv->remote_smileys == NULL)
891 return NULL;
893 return purple_smiley_list_get_by_shortcut(priv->remote_smileys, shortcut);
896 PurpleSmileyList *
897 purple_conversation_get_remote_smileys(PurpleConversation *conv)
899 PurpleConversationPrivate *priv = NULL;
901 g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
903 priv = purple_conversation_get_instance_private(conv);
904 return priv->remote_smileys;
908 /**************************************************************************
909 * GObject code
910 **************************************************************************/
912 /* Set method for GObject properties */
913 static void
914 purple_conversation_set_property(GObject *obj, guint param_id, const GValue *value,
915 GParamSpec *pspec)
917 PurpleConversation *conv = PURPLE_CONVERSATION(obj);
918 PurpleConversationPrivate *priv =
919 purple_conversation_get_instance_private(conv);
921 switch (param_id) {
922 case PROP_ACCOUNT:
923 priv->account = g_value_get_object(value);
924 break;
925 case PROP_NAME:
926 g_free(priv->name);
927 priv->name = g_value_dup_string(value);
928 break;
929 case PROP_TITLE:
930 g_free(priv->title);
931 priv->title = g_value_dup_string(value);
932 break;
933 case PROP_LOGGING:
934 purple_conversation_set_logging(conv, g_value_get_boolean(value));
935 break;
936 case PROP_FEATURES:
937 purple_conversation_set_features(conv, g_value_get_flags(value));
938 break;
939 default:
940 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
941 break;
945 /* Get method for GObject properties */
946 static void
947 purple_conversation_get_property(GObject *obj, guint param_id, GValue *value,
948 GParamSpec *pspec)
950 PurpleConversation *conv = PURPLE_CONVERSATION(obj);
952 switch (param_id) {
953 case PROP_ACCOUNT:
954 g_value_set_object(value, purple_conversation_get_account(conv));
955 break;
956 case PROP_NAME:
957 g_value_set_string(value, purple_conversation_get_name(conv));
958 break;
959 case PROP_TITLE:
960 g_value_set_string(value, purple_conversation_get_title(conv));
961 break;
962 case PROP_LOGGING:
963 g_value_set_boolean(value, purple_conversation_is_logging(conv));
964 break;
965 case PROP_FEATURES:
966 g_value_set_flags(value, purple_conversation_get_features(conv));
967 break;
968 default:
969 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
970 break;
974 static void
975 purple_conversation_init(PurpleConversation *conv)
979 /* Called when done constructing */
980 static void
981 purple_conversation_constructed(GObject *object)
983 PurpleConversation *conv = PURPLE_CONVERSATION(object);
984 PurpleAccount *account;
985 PurpleConnection *gc;
986 PurpleConversationUiOps *ops;
988 G_OBJECT_CLASS(purple_conversation_parent_class)->constructed(object);
990 g_object_get(object, "account", &account, NULL);
991 gc = purple_account_get_connection(account);
993 /* copy features from the connection. */
994 purple_conversation_set_features(conv, purple_connection_get_flags(gc));
996 /* add the conversation to the appropriate lists */
997 purple_conversations_add(conv);
999 /* Auto-set the title. */
1000 purple_conversation_autoset_title(conv);
1002 /* Don't move this.. it needs to be one of the last things done otherwise
1003 * it causes mysterious crashes on my system.
1004 * -- Gary
1006 ops = purple_conversations_get_ui_ops();
1007 purple_conversation_set_ui_ops(conv, ops);
1008 if (ops != NULL && ops->create_conversation != NULL)
1009 ops->create_conversation(conv);
1011 purple_signal_emit(purple_conversations_get_handle(),
1012 "conversation-created", conv);
1014 g_object_unref(account);
1017 static void
1018 purple_conversation_dispose(GObject *object)
1020 g_object_set_data(object, "is-finalizing", GINT_TO_POINTER(TRUE));
1023 /* GObject finalize function */
1024 static void
1025 purple_conversation_finalize(GObject *object)
1027 PurpleConversation *conv = PURPLE_CONVERSATION(object);
1028 PurpleConversationPrivate *priv =
1029 purple_conversation_get_instance_private(conv);
1030 PurpleConversationUiOps *ops = purple_conversation_get_ui_ops(conv);
1032 purple_request_close_with_handle(conv);
1034 purple_e2ee_state_unref(priv->e2ee_state);
1035 priv->e2ee_state = NULL;
1037 /* remove from conversations and im/chats lists prior to emit */
1038 purple_conversations_remove(conv);
1040 purple_signal_emit(purple_conversations_get_handle(),
1041 "deleting-conversation", conv);
1043 purple_conversation_close_logs(conv);
1044 purple_conversation_clear_message_history(conv);
1046 if (ops != NULL && ops->destroy_conversation != NULL)
1047 ops->destroy_conversation(conv);
1049 g_free(priv->name);
1050 g_free(priv->title);
1052 priv->name = NULL;
1053 priv->title = NULL;
1055 G_OBJECT_CLASS(purple_conversation_parent_class)->finalize(object);
1058 /* Class initializer function */
1059 static void
1060 purple_conversation_class_init(PurpleConversationClass *klass)
1062 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
1064 obj_class->dispose = purple_conversation_dispose;
1065 obj_class->finalize = purple_conversation_finalize;
1066 obj_class->constructed = purple_conversation_constructed;
1068 /* Setup properties */
1069 obj_class->get_property = purple_conversation_get_property;
1070 obj_class->set_property = purple_conversation_set_property;
1072 properties[PROP_ACCOUNT] = g_param_spec_object("account", "Account",
1073 "The account for the conversation.", PURPLE_TYPE_ACCOUNT,
1074 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1076 properties[PROP_NAME] = g_param_spec_string("name", "Name",
1077 "The name of the conversation.", NULL,
1078 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1080 properties[PROP_TITLE] = g_param_spec_string("title", "Title",
1081 "The title of the conversation.", NULL,
1082 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
1084 properties[PROP_LOGGING] = g_param_spec_boolean("logging", "Logging status",
1085 "Whether logging is enabled or not.", FALSE,
1086 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1088 properties[PROP_FEATURES] = g_param_spec_flags("features",
1089 "Connection features",
1090 "The connection features of the conversation.",
1091 PURPLE_TYPE_CONNECTION_FLAGS, 0,
1092 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1094 g_object_class_install_properties(obj_class, PROP_LAST, properties);